From e084709d9bf4bc3c9404906f19d7e9c456be5d7a Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Tue, 28 May 2024 10:38:55 +0200 Subject: [PATCH] Add the Command system with a simple help command. Progress towards #327 --- src/network.zig | 11 +-------- src/server/command/_command.zig | 42 +++++++++++++++++++++++++++++++++ src/server/command/_list.zig | 3 +++ src/server/command/help.zig | 42 +++++++++++++++++++++++++++++++++ src/server/server.zig | 23 +++++++++++++++++- 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 src/server/command/_command.zig create mode 100644 src/server/command/_list.zig create mode 100644 src/server/command/help.zig diff --git a/src/network.zig b/src/network.zig index 888510171..bd7eebd92 100644 --- a/src/network.zig +++ b/src/network.zig @@ -1140,16 +1140,7 @@ pub const Protocols = struct { pub const asynchronous = false; fn receive(conn: *Connection, data: []const u8) !void { if(conn.user) |user| { - if(data[0] == '/') { - // TODO: - // CommandExecutor.execute(data, user); - } else { - const newMessage = std.fmt.allocPrint(main.stackAllocator.allocator, "[{s}#ffffff]{s}", .{user.name, data}) catch unreachable; - defer main.stackAllocator.free(newMessage); - main.server.mutex.lock(); - defer main.server.mutex.unlock(); - main.server.sendMessage(newMessage); - } + main.server.messageFrom(data, user); } else { main.gui.windowlist.chat.addMessage(data); } diff --git a/src/server/command/_command.zig b/src/server/command/_command.zig new file mode 100644 index 000000000..0c8baca7d --- /dev/null +++ b/src/server/command/_command.zig @@ -0,0 +1,42 @@ +const std = @import("std"); + +const main = @import("root"); +const User = main.server.User; + +pub const Command = struct { + exec: *const fn(args: []const u8, source: *User) void, + name: []const u8, + description: []const u8, + usage: []const u8, +}; + +pub var commands: std.StringHashMap(Command) = undefined; + +pub fn init() void { + commands = std.StringHashMap(Command).init(main.globalAllocator.allocator); + const commandList = @import("_list.zig"); + inline for(@typeInfo(commandList).Struct.decls) |decl| { + commands.put(decl.name, .{ + .name = decl.name, + .description = @field(commandList, decl.name).description, + .usage = @field(commandList, decl.name).usage, + .exec = &@field(commandList, decl.name).execute, + }) catch unreachable; + } +} + +pub fn deinit() void { + commands.deinit(); +} + +pub fn execute(msg: []const u8, source: *User) void { + const end = std.mem.indexOfScalar(u8, msg, ' ') orelse msg.len; + const command = msg[0..end]; + if(commands.get(command)) |cmd| { + cmd.exec(msg[@min(end + 1, msg.len)..], source); + } else { + const result = std.fmt.allocPrint(main.stackAllocator.allocator, "#ff0000Unrecognized Command \"{s}\"", .{command}) catch unreachable; + defer main.stackAllocator.free(result); + source.sendMessage(result); + } +} \ No newline at end of file diff --git a/src/server/command/_list.zig b/src/server/command/_list.zig new file mode 100644 index 000000000..628c55696 --- /dev/null +++ b/src/server/command/_list.zig @@ -0,0 +1,3 @@ + + +pub const help = @import("help.zig"); \ No newline at end of file diff --git a/src/server/command/help.zig b/src/server/command/help.zig new file mode 100644 index 000000000..baa1464b3 --- /dev/null +++ b/src/server/command/help.zig @@ -0,0 +1,42 @@ +const std = @import("std"); + +const main = @import("root"); +const User = main.server.User; + +const command = @import("_command.zig"); + +pub const description = "Shows info about all the commands."; +pub const usage = "/help\n/help "; + +pub fn execute(args: []const u8, source: *User) void { + var msg = main.List(u8).init(main.stackAllocator); + defer msg.deinit(); + msg.appendSlice("#ffff00"); + if(args.len == 0) { + var iterator = command.commands.valueIterator(); + while(iterator.next()) |cmd| { + msg.appendSlice(cmd.name); + msg.appendSlice(": "); + msg.appendSlice(cmd.description); + msg.append('\n'); + } + } else { + var split = std.mem.splitScalar(u8, args, ' '); + while(split.next()) |arg| { + if(command.commands.get(arg)) |cmd| { + msg.appendSlice(cmd.name); + msg.appendSlice(": "); + msg.appendSlice(cmd.description); + msg.append('\n'); + msg.appendSlice(cmd.usage); + msg.append('\n'); + } else { + msg.appendSlice("#ff0000Unrecognized Command \""); + msg.appendSlice(arg); + msg.appendSlice("\"#ffff00\n"); + } + } + } + if(msg.items[msg.items.len - 1] == '\n') _ = msg.pop(); + source.sendMessage(msg.items); +} \ No newline at end of file diff --git a/src/server/server.zig b/src/server/server.zig index e4be3eb8f..70fce406a 100644 --- a/src/server/server.zig +++ b/src/server/server.zig @@ -14,6 +14,8 @@ pub const terrain = @import("terrain/terrain.zig"); pub const Entity = @import("Entity.zig"); pub const storage = @import("storage.zig"); +const command = @import("command/_command.zig"); + pub const User = struct { conn: *Connection = undefined, @@ -99,6 +101,10 @@ pub const User = struct { self.timeDifference.addDataPoint(time); self.interpolation.updatePosition(&position, &velocity, time); } + + pub fn sendMessage(user: *User, msg: []const u8) void { + main.network.Protocols.chat.send(user.conn, msg); + } }; pub const updatesPerSec: u32 = 20; @@ -119,6 +125,7 @@ pub var thread: ?std.Thread = null; fn init(name: []const u8) void { std.debug.assert(world == null); // There can only be one world. + command.init(); users = main.List(*User).init(main.globalAllocator); userDeinitList = main.List(*User).init(main.globalAllocator); lastTime = std.time.nanoTimestamp(); @@ -158,6 +165,7 @@ fn deinit() void { _world.deinit(); } world = null; + command.deinit(); } fn update() void { @@ -298,10 +306,23 @@ pub fn connect(user: *User) void { users.append(user); } +pub fn messageFrom(msg: []const u8, source: *User) void { + if(msg[0] == '/') { // Command. + std.log.info("User \"{s}\" executed command \"{s}\"", .{source.name, msg}); // TODO use color \033[0;32m + command.execute(msg[1..], source); + } else { + const newMessage = std.fmt.allocPrint(main.stackAllocator.allocator, "[{s}#ffffff]{s}", .{source.name, msg}) catch unreachable; + defer main.stackAllocator.free(newMessage); + main.server.mutex.lock(); + defer main.server.mutex.unlock(); + main.server.sendMessage(newMessage); + } +} + pub fn sendMessage(msg: []const u8) void { main.utils.assertLocked(&mutex); std.log.info("Chat: {s}", .{msg}); // TODO use color \033[0;32m for(users.items) |user| { - main.network.Protocols.chat.send(user.conn, msg); + user.sendMessage(msg); } } \ No newline at end of file