initial commit
This commit is contained in:
parent
50b67e7b33
commit
08d9e7680a
|
@ -0,0 +1,4 @@
|
|||
.DS_Store
|
||||
all.sh
|
||||
zig-cache/
|
||||
zig-out/
|
|
@ -0,0 +1,35 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const exe = b.addExecutable("minecraft_server_zig", "src/main.zig");
|
||||
exe.use_stage1 = true; // needed for zig v0.10 +
|
||||
exe.setTarget(target);
|
||||
exe.setBuildMode(mode);
|
||||
exe.install();
|
||||
|
||||
const run_cmd = exe.run();
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const exe_tests = b.addTest("src/main.zig");
|
||||
exe_tests.setTarget(target);
|
||||
exe_tests.setBuildMode(mode);
|
||||
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&exe_tests.step);
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
const std = @import("std");
|
||||
const net = std.net;
|
||||
const mem = std.mem;
|
||||
|
||||
const Queue = std.atomic.Queue;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
|
||||
pub const io_mode = .evented;
|
||||
|
||||
const json = "{'version':{'name':'The Shadow?','protocol':762},'players':{'max':25,'online':0,'sample':[]},'description':{'text':'Minecraft Server in Zig'}";
|
||||
|
||||
const SEGMENT_BITS: i32 = 0x7F;
|
||||
const CONTINUE_BIT: i32 = 0x80;
|
||||
|
||||
fn varint2int(buf: []u8) struct { val: i32, bytes: usize } {
|
||||
var i: usize = 0;
|
||||
var value: i32 = 0;
|
||||
var position: u5 = 0;
|
||||
var currentByte: u8 = undefined;
|
||||
|
||||
while (true) {
|
||||
currentByte = buf[i];
|
||||
value |= (currentByte & SEGMENT_BITS) << position;
|
||||
|
||||
if ((currentByte & CONTINUE_BIT) == 0) break;
|
||||
|
||||
i += 1;
|
||||
position += 7;
|
||||
|
||||
if (position >= 32) unreachable;
|
||||
}
|
||||
|
||||
return .{ .val = value, .bytes = i + 1 };
|
||||
}
|
||||
|
||||
fn int2varint(stream: net.Stream, value: i32) void {
|
||||
while (true) {
|
||||
if ((value & ~SEGMENT_BITS) == 0) {
|
||||
stream.write(value);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.write((value & @as(i32, SEGMENT_BITS)) | @as(i32, CONTINUE_BIT));
|
||||
|
||||
value >>= 7;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
var allocator = general_purpose_allocator.allocator();
|
||||
|
||||
var server = net.StreamServer.init(.{ .reuse_address = true });
|
||||
defer server.deinit();
|
||||
|
||||
var world = World{ .clients = std.AutoHashMap(*Client, void).init(allocator) };
|
||||
|
||||
try server.listen(net.Address.parseIp("0.0.0.0", 25565) catch unreachable);
|
||||
std.log.info("minecraft server hosted at {}\n", .{server.listen_address});
|
||||
|
||||
var cleanup = &Queue(*ArenaAllocator).init();
|
||||
_ = async cleaner(cleanup);
|
||||
|
||||
while (true) {
|
||||
var client_arena = ArenaAllocator.init(allocator);
|
||||
const client = try client_arena.allocator().create(Client);
|
||||
client.* = Client{
|
||||
.stream = (try server.accept()).stream,
|
||||
.handle_frame = async client.handle(&world, cleanup, &client_arena),
|
||||
};
|
||||
try world.clients.putNoClobber(client, {});
|
||||
}
|
||||
}
|
||||
|
||||
const Client = struct {
|
||||
stream: net.Stream,
|
||||
handle_frame: @Frame(handle),
|
||||
login_state: 0,
|
||||
input_buffer: [2097151]u8,
|
||||
output_buffer: [2097151]u8,
|
||||
|
||||
fn handle(self: *Client, world: *World, cleanup: *Queue(*ArenaAllocator), arena: *ArenaAllocator) !void {
|
||||
_ = world;
|
||||
const stream = self.stream;
|
||||
defer {
|
||||
stream.close();
|
||||
var node = Queue(*ArenaAllocator).Node{ .data = arena, .next = undefined, .prev = undefined };
|
||||
cleanup.put(&node);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var ptr: usize = 0;
|
||||
const msg_size = try stream.read(self.input_buffer[0..]);
|
||||
std.log.info("{d}", .{msg_size});
|
||||
const size = varint2int(self.input_buffer[0..msg_size]);
|
||||
ptr = size.bytes;
|
||||
std.log.info("{d}", .{size.val});
|
||||
const packet_id = varint2int(self.input_buffer[ptr..@intCast(usize, size.val)]);
|
||||
std.log.info("{d}", .{packet_id.val});
|
||||
ptr += packet_id.bytes;
|
||||
switch (packet_id.val) {
|
||||
0x00 => {
|
||||
switch (self.login_state) {
|
||||
0 => {
|
||||
const protocol_version = varint2int(self.input_buffer[ptr..msg_size]);
|
||||
std.log.info("protocol_version={d}", .{protocol_version.val});
|
||||
ptr += protocol_version.bytes;
|
||||
const server_address_len = varint2int(self.input_buffer[ptr..msg_size]);
|
||||
ptr += server_address_len.bytes;
|
||||
std.log.info("server_address={s}", .{self.input_buffer[ptr .. ptr + @intCast(usize, server_address_len.val)]});
|
||||
ptr += @intCast(u64, server_address_len.val);
|
||||
const u16size = @intCast(usize, @sizeOf(u16));
|
||||
const server_port = mem.nativeToBig(i16, mem.bytesAsSlice(i16, self.input_buffer[ptr .. ptr + u16size])[0]);
|
||||
std.log.info("server_port{d}", .{server_port});
|
||||
ptr += @sizeOf(u16);
|
||||
const next_state = varint2int(self.input_buffer[ptr..msg_size]);
|
||||
std.log.info("next_state={d}", .{next_state.val});
|
||||
self.login_state = next_state.val;
|
||||
},
|
||||
1 => {
|
||||
var fbs = std.io.fixedBufferStream(&self.output_buffer);
|
||||
_ = fbs;
|
||||
},
|
||||
2 => {},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
0x01 => {},
|
||||
else => {},
|
||||
}
|
||||
|
||||
//world.broadcast(buf[0..n], self);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const World = struct {
|
||||
clients: std.AutoHashMap(*Client, void),
|
||||
|
||||
fn broadcast(world: *World, msg: []const u8, sender: *Client) void {
|
||||
var it = world.clients.keyIterator();
|
||||
while (it.next()) |key_ptr| {
|
||||
const client = key_ptr.*;
|
||||
if (client == sender) continue;
|
||||
_ = client.stream.write(msg) catch |e| std.log.warn("unable to send: {}\n", .{e});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn cleaner(cleanup: *Queue(*ArenaAllocator)) !void {
|
||||
while (true) {
|
||||
while (cleanup.get()) |node| {
|
||||
node.data.deinit(); // the client arena allocator
|
||||
}
|
||||
// 5 seconds (in nano seconds)
|
||||
std.time.sleep(5000000000);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue