semi synced multiplayer
This commit is contained in:
@@ -1,126 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
MsgKeyPress uint16 = 1
|
|
||||||
MsgGameState uint16 = 2
|
|
||||||
MSGServerStatus uint16 = 3
|
|
||||||
MSGOnConnect uint16 = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
var num_clients int = 0
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
num_clients += 1
|
|
||||||
fmt.Println(toString(num_clients))
|
|
||||||
go SendPacket(conn, MSGOnConnect, []byte(string(num_clients)))
|
|
||||||
|
|
||||||
go handle(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toString(num int) {
|
|
||||||
return strconv.FormatInt(int64(num), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,11 +4,14 @@ local network = {}
|
|||||||
|
|
||||||
local client
|
local client
|
||||||
|
|
||||||
|
client_id = 0
|
||||||
|
|
||||||
-- Packet IDs
|
-- Packet IDs
|
||||||
network.MSG_KEY_PRESS = 1
|
network.MSG_KEY_PRESS = 1
|
||||||
network.MSG_GAMESTATE = 2
|
network.MSG_GAMESTATE = 2
|
||||||
network.MSG_SERVER_STATUS = 3
|
network.MSG_SERVER_STATUS = 3
|
||||||
network.MSG_ON_CONNECT = 4
|
network.MSG_ON_CONNECT = 4
|
||||||
|
network.MSG_ON_PLAYER_CONNECT = 5
|
||||||
|
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
-- Packing
|
-- Packing
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
utils = {}
|
||||||
|
|
||||||
|
function vec(x, y)
|
||||||
|
return {x = x, y = y}
|
||||||
|
end
|
||||||
|
|
||||||
|
function utils.round(num)
|
||||||
|
return num >= 0 and math.floor(num + 0.5) or math.ceil(num - 0.5)
|
||||||
|
end
|
||||||
+7
-7
@@ -1,9 +1,15 @@
|
|||||||
|
require("client.utils")
|
||||||
world = {}
|
world = {}
|
||||||
world.tiles = {}
|
world.tiles = {}
|
||||||
|
world.entities = {}
|
||||||
|
world.entities.com = {}
|
||||||
|
world.entities.players = {}
|
||||||
world.utils = {}
|
world.utils = {}
|
||||||
|
|
||||||
|
world.cell_size = 10 -- default
|
||||||
|
|
||||||
function world.utils.to_tile(x, y)
|
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)
|
return vec(utils.round(x / world.cell_size) * world.cell_size, utils.round(y / world.cell_size) * world.cell_size)
|
||||||
end
|
end
|
||||||
|
|
||||||
function world.utils.set_tile(x, y, tbl)
|
function world.utils.set_tile(x, y, tbl)
|
||||||
@@ -15,9 +21,3 @@ function world.utils.get_tile(x, y)
|
|||||||
local key = x .. "," .. y
|
local key = x .. "," .. y
|
||||||
return world.tiles[key]
|
return world.tiles[key]
|
||||||
end
|
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
|
|
||||||
|
|||||||
@@ -1,29 +1,83 @@
|
|||||||
local network = require("client")
|
local network = require("client")
|
||||||
|
require("client.world")
|
||||||
|
|
||||||
local player = {
|
function move(player, x, y)
|
||||||
x = 100,
|
local dir = strvec_to_intvec(x,y)
|
||||||
y = 100,
|
player.x = dir.x
|
||||||
}
|
player.y = dir.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function dir_to_vec(dir)
|
||||||
|
if dir == "up" then
|
||||||
|
return vec(0,-1)
|
||||||
|
elseif dir == "down" then
|
||||||
|
return vec(0,1)
|
||||||
|
elseif dir == "left" then
|
||||||
|
return vec(-1,0)
|
||||||
|
elseif dir == "right" then
|
||||||
|
return vec(1,0)
|
||||||
|
else
|
||||||
|
return vec(0,0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function strvec_to_intvec(x,y)
|
||||||
|
return vec(tonumber(x), tonumber(y))
|
||||||
|
end
|
||||||
|
|
||||||
local client_id = 0
|
|
||||||
|
|
||||||
function love.load()
|
function love.load()
|
||||||
network.connect("127.0.0.1", 9000)
|
network.connect("127.0.0.1", 9000)
|
||||||
|
|
||||||
network.on(network.MSG_ON_CONNECT, function(payload)
|
network.on(network.MSG_ON_CONNECT, function(payload)
|
||||||
print("connected to server with id " .. payload)
|
print("connected to server with id " .. payload)
|
||||||
|
add_player(payload)
|
||||||
if client_id == 0 then
|
if client_id == 0 then
|
||||||
client_id = payload
|
client_id = payload
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
network.on(network.MSG_GAMESTATE, function(payload)
|
network.on(network.MSG_GAMESTATE, function(payload)
|
||||||
print(payload)
|
local cmds = read_command(payload)
|
||||||
|
act_on_command(cmds)
|
||||||
|
end)
|
||||||
|
|
||||||
|
network.on(network.MSG_ON_PLAYER_CONNECT, function(payload)
|
||||||
|
if payload == client_id then return end
|
||||||
|
add_player(payload)
|
||||||
|
for i, v in pairs(world.entities.players) do
|
||||||
|
network.sendPacket(network.MSG_KEY_PRESS, "player/" .. v.client_id .. "/moved/" .. v.x .. "/".. v.y)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function add_player(id)
|
||||||
|
local new_player = {
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
sprite = 0,
|
||||||
|
client_id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
world.entities.players[new_player.client_id] = new_player
|
||||||
|
|
||||||
|
return new_player
|
||||||
|
end
|
||||||
|
|
||||||
function love.keypressed(key)
|
function love.keypressed(key)
|
||||||
network.sendPacket(network.MSG_KEY_PRESS, client_id .. "/pressed/" .. key)
|
local dir
|
||||||
|
if key == "w" then
|
||||||
|
dir = vec(0,-world.cell_size)
|
||||||
|
elseif key == "a" then
|
||||||
|
dir = vec(-world.cell_size,0)
|
||||||
|
elseif key == "s" then
|
||||||
|
dir = vec(0,world.cell_size)
|
||||||
|
elseif key == "d" then
|
||||||
|
dir = vec(world.cell_size,0)
|
||||||
|
else
|
||||||
|
dir = "right"
|
||||||
|
end
|
||||||
|
network.sendPacket(network.MSG_KEY_PRESS, "player/" .. client_id .. "/moved/" .. world.entities.players[client_id].x + dir.x .. "/".. world.entities.players[client_id].y + dir.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.update()
|
function love.update()
|
||||||
@@ -31,7 +85,46 @@ function love.update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
love.graphics.print("Connected as id " .. client_id, 20, 20)
|
for i, v in pairs(world.entities.players) do
|
||||||
|
love.graphics.rectangle("fill", v.x, v.y, world.cell_size, world.cell_size)
|
||||||
|
love.graphics.print(v.client_id, v.x, v.y - world.cell_size - 5)
|
||||||
|
end
|
||||||
|
|
||||||
love.graphics.circle("fill", player.x, player.y, 10)
|
end
|
||||||
|
|
||||||
|
function read_command(payload)
|
||||||
|
local cmds = {}
|
||||||
|
local num_cmds = 1
|
||||||
|
|
||||||
|
local s = ""
|
||||||
|
for i in string.gmatch(payload, ".") do
|
||||||
|
if i ~= "/" then
|
||||||
|
s = s .. i
|
||||||
|
else
|
||||||
|
cmds[num_cmds] = s
|
||||||
|
s = ""
|
||||||
|
num_cmds = num_cmds + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cmds[num_cmds] = s
|
||||||
|
return cmds
|
||||||
|
end
|
||||||
|
|
||||||
|
function act_on_command(cmds)
|
||||||
|
if cmds[1] == "player" then
|
||||||
|
-- do player sync
|
||||||
|
for i, v in pairs(world.entities.players) do
|
||||||
|
if v.client_id == cmds[2] then
|
||||||
|
if cmds[3] == "moved" then
|
||||||
|
local player = world.entities.players[cmds[2]]
|
||||||
|
move(player, tonumber(cmds[4]),tonumber(cmds[5]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if world.entities.players[cmds[2]] == nil then
|
||||||
|
print("adding player")
|
||||||
|
add_player(cmds[2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+25
-10
@@ -10,14 +10,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MsgKeyPress uint16 = 1
|
MsgKeyPress uint16 = 1
|
||||||
MsgGameState uint16 = 2
|
MsgGameState uint16 = 2
|
||||||
MSGServerStatus uint16 = 3
|
MSGServerStatus uint16 = 3
|
||||||
MSGOnConnect uint16 = 4
|
MSGOnConnect uint16 = 4
|
||||||
|
MSGOnPlayerConnect uint16 = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
var num_clients int = 0
|
var num_clients int = 0
|
||||||
var clients = map[int]net.Conn{}
|
var clients = make(map[string]client)
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
x uint
|
||||||
|
y uint
|
||||||
|
client_id string
|
||||||
|
conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
func Start() {
|
func Start() {
|
||||||
listener, err := net.Listen("tcp", ":9000")
|
listener, err := net.Listen("tcp", ":9000")
|
||||||
@@ -34,18 +42,25 @@ func Start() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// client connects
|
||||||
num_clients += 1
|
num_clients += 1
|
||||||
client_id := strconv.Itoa(num_clients)
|
client_id := strconv.Itoa(num_clients)
|
||||||
fmt.Println(client_id)
|
fmt.Println(client_id)
|
||||||
go SendPacket(MSGOnConnect, []byte(client_id))
|
go SendPacket(MSGOnConnect, []byte(client_id))
|
||||||
addClient(conn, num_clients)
|
go SendPacket(MSGOnPlayerConnect, []byte(client_id))
|
||||||
|
|
||||||
|
addClient(conn, client_id)
|
||||||
|
|
||||||
go handle(conn)
|
go handle(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addClient(conn net.Conn, id int) {
|
func addClient(conn net.Conn, id string) {
|
||||||
clients[id] = conn
|
var new_client client = client{
|
||||||
|
client_id: id,
|
||||||
|
conn: conn,
|
||||||
|
}
|
||||||
|
clients[id] = new_client
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendPacket(msgID uint16, payload []byte) error {
|
func SendPacket(msgID uint16, payload []byte) error {
|
||||||
@@ -57,8 +72,8 @@ func SendPacket(msgID uint16, payload []byte) error {
|
|||||||
binary.BigEndian.PutUint16(buf[4:6], msgID)
|
binary.BigEndian.PutUint16(buf[4:6], msgID)
|
||||||
copy(buf[6:], payload)
|
copy(buf[6:], payload)
|
||||||
|
|
||||||
for _, conn := range clients {
|
for _, client := range clients {
|
||||||
n, err := conn.Write(buf)
|
n, err := client.conn.Write(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user