commit bc14e946fc1e78cae94d9e8661c61cb4285ba810 Author: wooshings Date: Mon Jun 29 11:28:51 2026 -0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2dcf1d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +export/ diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..3448506 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + "runtime.version": "Lua 5.4", + "runtime.nonstandardSymbol": ["+=", "-=", "*=", "/=", "%="], + "workspace.library": ["${3rd}/love2d/library"], + "format.defaultConfig": { + "indent_style": "space", + "indent_size": "2" + }, + "diagnostics": { + "disable": ["lowercase-global"] + } +} diff --git a/client/init.lua b/client/init.lua new file mode 100644 index 0000000..3cc50e6 --- /dev/null +++ b/client/init.lua @@ -0,0 +1,142 @@ +local socket = require("socket") + +local network = {} + +local client + +-- Packet IDs +network.MSG_KEY_PRESS = 1 +network.MSG_GAMESTATE = 2 + +-------------------------------------------------- +-- Packing +-------------------------------------------------- + +local function packU16(n) + return string.char(math.floor(n / 256) % 256, n % 256) +end + +local function packU32(n) + return string.char(math.floor(n / 16777216) % 256, math.floor(n / 65536) % 256, math.floor(n / 256) % 256, n % 256) +end + +local function unpackU16(s) + local b1, b2 = s:byte(1, 2) + return b1 * 256 + b2 +end + +local function unpackU32(s) + local b1, b2, b3, b4 = s:byte(1, 4) + + return b1 * 16777216 + b2 * 65536 + b3 * 256 + b4 +end + +-------------------------------------------------- +-- Connection +-------------------------------------------------- + +function network.connect(ip, port) + client = assert(socket.tcp()) + + client:settimeout(0) + + local ok, err = client:connect(ip, port) + + -- Non-blocking connect is expected + if not ok and err ~= "timeout" then + error(err) + end + + print("Connecting...") +end + +-------------------------------------------------- +-- Sending +-------------------------------------------------- + +function network.sendPacket(id, payload) + if not client then + return + end + + payload = payload or "" + + local length = 2 + #payload + + local packet = packU32(length) .. packU16(id) .. payload + + client:send(packet) +end + +function network.sendKey(key) + network.sendPacket(network.MSG_KEY_PRESS, key) +end + +-------------------------------------------------- +-- Packet handlers +-------------------------------------------------- + +network.handlers = {} + +function network.on(id, callback) + network.handlers[id] = callback +end + +-------------------------------------------------- +-- Receiving +-------------------------------------------------- + +local function receivePacket() + local lenData, err = client:receive(4) + + if not lenData then + return nil + end + + local length = unpackU32(lenData) + + local idData = client:receive(2) + if not idData then + return nil + end + + local msgID = unpackU16(idData) + + local payload = "" + + if length > 2 then + payload = client:receive(length - 2) + + if not payload then + return nil + end + end + + return msgID, payload +end + +-------------------------------------------------- +-- Poll every frame +-------------------------------------------------- + +function network.update() + if not client then + return + end + + while true do + local id, payload = receivePacket() + + if not id then + break + end + + local handler = network.handlers[id] + + if handler then + handler(payload) + end + end +end + +return network diff --git a/client/tile.lua b/client/tile.lua new file mode 100644 index 0000000..c164b2b --- /dev/null +++ b/client/tile.lua @@ -0,0 +1 @@ +tile = {} diff --git a/client/world.lua b/client/world.lua new file mode 100644 index 0000000..58cad25 --- /dev/null +++ b/client/world.lua @@ -0,0 +1,23 @@ +world = {} +world.tiles = {} +world.utils = {} + +function world.utils.to_tile(x, y) + return vec(util.round(x / world.cell_size) * world.cell_size, util.round(y / world.cell_size) * world.cell_size) +end + +function world.utils.set_tile(x, y, tbl) + local key = x .. "," .. y + world.tiles[key] = tbl +end + +function world.utils.get_tile(x, y) + local key = x .. "," .. y + return world.tiles[key] +end + +function world:draw() + for i, v in pairs(world.tiles) do + gfx.rect_fill(v.aabb.x, v.aabb.y, v.aabb.w, v.aabb.h, gfx.COLOR_DARK_GRAY) + end +end diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cb1b0f5 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module grid-mmo-server + +go 1.26.4 diff --git a/main.go b/main.go new file mode 100644 index 0000000..14a6c94 --- /dev/null +++ b/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "grid-mmo-server/server" +) + +type world struct { + tiles [][]tile +} + +type tile struct { +} + +func main() { + server.Start() +} diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..766bd63 --- /dev/null +++ b/main.lua @@ -0,0 +1,28 @@ +local network = require("client") + +local player = { + x = 100, + y = 100, +} + +function love.load() + network.connect("127.0.0.1", 9000) + + network.on(network.MSG_GAMESTATE, function(payload) + print("Server:", payload) + end) +end + +function love.keypressed(key) + network.sendKey(key) +end + +function love.update() + network.update() +end + +function love.draw() + love.graphics.print("Connected", 20, 20) + + love.graphics.circle("fill", player.x, player.y, 10) +end diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..a1b3dea --- /dev/null +++ b/server/server.go @@ -0,0 +1,115 @@ +package server + +import ( + "encoding/binary" + "fmt" + "io" + "log" + "net" +) + +const ( + MsgKeyPress uint16 = 1 + MsgGameState uint16 = 2 +) + +func Start() { + listener, err := net.Listen("tcp", ":9000") + if err != nil { + log.Fatal(err) + } + + fmt.Println("Server listening on port 9000") + + for { + conn, err := listener.Accept() + if err != nil { + fmt.Println("Accept error:", err) + continue + } + + fmt.Println("Client connected") + + go handle(conn) + } +} + +func SendPacket(conn net.Conn, msgID uint16, payload []byte) error { + // Length matches Lua: 2 (msgID) + payload size + length := uint32(2 + len(payload)) + buf := make([]byte, 4+2+len(payload)) + + binary.BigEndian.PutUint32(buf[0:4], length) + binary.BigEndian.PutUint16(buf[4:6], msgID) + copy(buf[6:], payload) + + n, err := conn.Write(buf) + if err != nil { + return err + } + if n != len(buf) { + return fmt.Errorf("short write: expected %d bytes, got %d", len(buf), n) + } + return nil +} + +func handle(conn net.Conn) { + defer conn.Close() + + for { + // Read packet length + var length uint32 + + err := binary.Read(conn, binary.BigEndian, &length) + if err != nil { + if err != io.EOF { + fmt.Println("Read length error:", err) + } + + fmt.Println("Client disconnected") + return + } + + // Prevent invalid packets + if length < 2 { + fmt.Println("Invalid packet length:", length) + return + } + + // Read message ID + var msgID uint16 + + err = binary.Read(conn, binary.BigEndian, &msgID) + if err != nil { + fmt.Println("Read message ID error:", err) + return + } + + // Read payload + payload := make([]byte, length-2) + + _, err = io.ReadFull(conn, payload) + if err != nil { + fmt.Println("Read payload error:", err) + return + } + + switch msgID { + + case MsgKeyPress: + msg := string(payload) + + fmt.Println(msg) + + // Send something back to the client + err := SendPacket(conn, MsgGameState, []byte("received key press: "+msg)) + if err != nil { + fmt.Println("Send error:", err) + return + } + + default: + fmt.Println("Unknown message:", msgID) + } + } +}