local Multiplayer = {} local binser = require("lib.binser") local pprint = require("lib.pprint") local uid = require("helpers.uid") local RATE_LIMIT = 20 local CMD = { SPAWN_PLAYER = 1, MOVE_PLAYER = 2, AIM_PLAYER = 3, PLAYER_SHOT = 4, } local function removeValue(t, v) for i, vv in ipairs(t) do if v == vv then table.remove(t, i) end end end function Multiplayer:init() self.connected_peers = {} local host_socket = self.pool.data.host_socket for i=1, host_socket:peer_count() do local peer = host_socket:get_peer(i) if peer:state() == "connected" then self:onPeerConnect(peer) end end end function Multiplayer:onPeerConnect(peer) table.insert(self.connected_peers, peer:index()) end function Multiplayer:onPeerDisconnect(peer) local peer_index = peer:index() removeValue(self.connected_peers, peer_index) for _, player in ipairs(self.pool.groups.player.entities) do if player.peer_index == peer_index then self.pool:removeEntity(player) end end end function Multiplayer:disconnectPeers() local host_socket = self.pool.data.host_socket for _, index in ipairs(self.connected_peers) do local peer = host_socket:get_peer(index) peer:disconnect_now() self:onPeerDisconnect(peer) end host_socket:flush() end function Multiplayer:sendToPeer(peer, ...) peer:send(binser.serialize(...), nil, "unreliable") end function Multiplayer:sendToAllPeers(...) local payload = binser.serialize(...) local host_socket = self.pool.data.host_socket for _, index in ipairs(self.connected_peers) do local peer = host_socket:get_peer(index) peer:send(payload, nil, "unreliable") end end function Multiplayer:update() local host_socket = self.pool.data.host_socket local event = host_socket:service() if event then if event.type == "connect" then self:onPeerConnect(event.peer) elseif event.type == "disconnect" then self:onPeerDisconnect(event.peer) elseif event.type == "receive" then local parameters = binser.deserialize(event.data) self:handlePeerMessage(event.peer, unpack(parameters)) end end end function Multiplayer:getPlayerEntityById(id) for _, player in ipairs(self.pool.groups.player.entities) do if player.id == id then return player end end end function Multiplayer:handlePeerMessage(peer, cmd, ...) if cmd == CMD.SPAWN_PLAYER then local id = ... local PlayerSystem = self.pool:getSystem(require("systems.player")) local player = PlayerSystem:spawnPlayer() player.id = id player.peer_index = peer:index() elseif cmd == CMD.MOVE_PLAYER then local id, move_dir, pos = ... local player = self:getPlayerEntityById(id) if player and player.peer_index == peer:index() then player.move_dir = move_dir player.pos = pos end elseif cmd == CMD.AIM_PLAYER then local id, aim_dir = ... local player = self:getPlayerEntityById(id) if player and player.peer_index == peer:index() then player.aim_dir = aim_dir end elseif cmd == CMD.PLAYER_SHOT then local id = ... local player = self:getPlayerEntityById(id) if player and player.peer_index == peer:index() then self.pool:emit("tryShootingBolt", player) end else print("DEBUG INFORMATION:") print("Message:") pprint{cmd, ...} error("Unhandled message from peer") end end function Multiplayer:addToGroup(group, player) if group == "player" and not player.peer_index then player.id = uid() self:sendToAllPeers(CMD.SPAWN_PLAYER, player.id) self.pool:queue(player) end end function Multiplayer:playerMoved(player) self:sendToAllPeers(CMD.MOVE_PLAYER, player.id, player.move_dir, player.pos) end function Multiplayer:playerAimed(player) self:sendToAllPeers(CMD.AIM_PLAYER, player.id, player.aim_dir) end function Multiplayer:playerShot(player) self:sendToAllPeers(CMD.PLAYER_SHOT, player.id) end return Multiplayer