mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-02 02:23:56 +00:00
Add local relay server and apply formatting fixes
This commit is contained in:
parent
fb1d596704
commit
21a12d480c
@ -12,28 +12,21 @@
|
||||
#include "mxgeometry/mxgeometry3d.h"
|
||||
#include "realtime/realtime.h"
|
||||
#include "roi/legoroi.h"
|
||||
#include <vec.h>
|
||||
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#include <cmath>
|
||||
#include <vec.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace Multiplayer;
|
||||
|
||||
// clang-format off
|
||||
static const char* g_vehicleROINames[VEHICLE_COUNT] = {
|
||||
"copter", "jsuser", "dunebugy", "bike", "board", "moto", "towtk", "ambul"
|
||||
};
|
||||
static const char* g_vehicleROINames[VEHICLE_COUNT] =
|
||||
{"copter", "jsuser", "dunebugy", "bike", "board", "moto", "towtk", "ambul"};
|
||||
|
||||
static const char* g_rideAnimNames[VEHICLE_COUNT] = {
|
||||
NULL, NULL, NULL, "CNs001Bd", "CNs001sk", "CNs011Ni", NULL, NULL
|
||||
};
|
||||
static const char* g_rideAnimNames[VEHICLE_COUNT] = {NULL, NULL, NULL, "CNs001Bd", "CNs001sk", "CNs011Ni", NULL, NULL};
|
||||
|
||||
static const char* g_rideVehicleROINames[VEHICLE_COUNT] = {
|
||||
NULL, NULL, NULL, "bikebd", "board", "motoni", NULL, NULL
|
||||
};
|
||||
// clang-format on
|
||||
static const char* g_rideVehicleROINames[VEHICLE_COUNT] = {NULL, NULL, NULL, "bikebd", "board", "motoni", NULL, NULL};
|
||||
|
||||
static bool IsLargeVehicle(int8_t p_vehicleType)
|
||||
{
|
||||
@ -43,10 +36,9 @@ static bool IsLargeVehicle(int8_t p_vehicleType)
|
||||
RemotePlayer::RemotePlayer(uint32_t p_peerId, uint8_t p_actorId)
|
||||
: m_peerId(p_peerId), m_actorId(p_actorId), m_roi(nullptr), m_spawned(false), m_visible(false), m_targetSpeed(0.0f),
|
||||
m_targetVehicleType(VEHICLE_NONE), m_targetWorldId(-1), m_lastUpdateTime(SDL_GetTicks()),
|
||||
m_hasReceivedUpdate(false), m_walkAnim(nullptr), m_walkRoiMap(nullptr), m_walkRoiMapSize(0),
|
||||
m_animTime(0.0f), m_idleTime(0.0f), m_wasMoving(false), m_idleAnim(nullptr), m_idleRoiMap(nullptr),
|
||||
m_idleRoiMapSize(0), m_idleAnimTime(0.0f), m_rideAnim(nullptr), m_rideRoiMap(nullptr),
|
||||
m_rideRoiMapSize(0), m_rideVehicleROI(nullptr),
|
||||
m_hasReceivedUpdate(false), m_walkAnim(nullptr), m_walkRoiMap(nullptr), m_walkRoiMapSize(0), m_animTime(0.0f),
|
||||
m_idleTime(0.0f), m_wasMoving(false), m_idleAnim(nullptr), m_idleRoiMap(nullptr), m_idleRoiMapSize(0),
|
||||
m_idleAnimTime(0.0f), m_rideAnim(nullptr), m_rideRoiMap(nullptr), m_rideRoiMapSize(0), m_rideVehicleROI(nullptr),
|
||||
m_vehicleROI(nullptr), m_currentVehicleType(VEHICLE_NONE)
|
||||
{
|
||||
SDL_snprintf(m_uniqueName, sizeof(m_uniqueName), "%s_mp_%u", LegoActor::GetActorName(p_actorId), p_peerId);
|
||||
@ -412,7 +404,12 @@ void RemotePlayer::UpdateAnimation(float p_deltaTime)
|
||||
MxMatrix transform(m_roi->GetLocal2World());
|
||||
LegoTreeNode* root = m_idleAnim->GetRoot();
|
||||
for (LegoU32 i = 0; i < root->GetNumChildren(); i++) {
|
||||
LegoROI::ApplyAnimationTransformation(root->GetChild(i), transform, (LegoTime) timeInCycle, m_idleRoiMap);
|
||||
LegoROI::ApplyAnimationTransformation(
|
||||
root->GetChild(i),
|
||||
transform,
|
||||
(LegoTime) timeInCycle,
|
||||
m_idleRoiMap
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
extensions/src/multiplayer/server/.gitignore
vendored
Normal file
2
extensions/src/multiplayer/server/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.wrangler/
|
||||
node_modules/
|
||||
33
extensions/src/multiplayer/server/package-lock.json
generated
Normal file
33
extensions/src/multiplayer/server/package-lock.json
generated
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "server",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"ws": "^8.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
extensions/src/multiplayer/server/package.json
Normal file
5
extensions/src/multiplayer/server/package.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"ws": "^8.19.0"
|
||||
}
|
||||
}
|
||||
104
extensions/src/multiplayer/server/relay-local.mjs
Normal file
104
extensions/src/multiplayer/server/relay-local.mjs
Normal file
@ -0,0 +1,104 @@
|
||||
import { createServer } from "http";
|
||||
import { WebSocketServer } from "ws";
|
||||
|
||||
const PORT = process.env.PORT || 8787;
|
||||
const rooms = new Map();
|
||||
|
||||
function getRoom(roomId) {
|
||||
if (!rooms.has(roomId)) {
|
||||
rooms.set(roomId, { connections: new Map(), nextPeerId: 1 });
|
||||
}
|
||||
return rooms.get(roomId);
|
||||
}
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
if (req.url === "/" || req.url === "/health") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "ok" }));
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end("Not Found");
|
||||
}
|
||||
});
|
||||
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
wss.on("connection", (ws, req) => {
|
||||
const pathParts = (req.url || "").split("/").filter(Boolean);
|
||||
if (pathParts.length !== 2 || pathParts[0] !== "room") {
|
||||
console.log(`[REJECT] Invalid path: ${req.url}`);
|
||||
ws.close(1008, "Invalid path");
|
||||
return;
|
||||
}
|
||||
|
||||
const roomId = pathParts[1];
|
||||
const room = getRoom(roomId);
|
||||
const peerId = room.nextPeerId++;
|
||||
const peerIdStr = String(peerId);
|
||||
room.connections.set(peerIdStr, ws);
|
||||
console.log(`[CONNECT] Peer ${peerId} joined room "${roomId}" (${room.connections.size} peers)`);
|
||||
|
||||
// Send the peer its assigned ID as the first message
|
||||
const idMsg = Buffer.alloc(5);
|
||||
idMsg.writeUInt8(0xff, 0);
|
||||
idMsg.writeUInt32LE(peerId, 1);
|
||||
ws.send(idMsg);
|
||||
|
||||
ws.on("message", (data) => {
|
||||
if (!(data instanceof Buffer) || data.length < 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
const msgType = data.readUInt8(0);
|
||||
console.log(`[MSG] Peer ${peerId} sent type=${msgType} len=${data.length}`);
|
||||
|
||||
// Stamp the peerId into the message header (bytes 1-4)
|
||||
const stamped = Buffer.from(data);
|
||||
stamped.writeUInt32LE(peerId, 1);
|
||||
|
||||
// Broadcast to all other peers in this room
|
||||
let sent = 0;
|
||||
for (const [id, peer] of room.connections) {
|
||||
if (id !== peerIdStr) {
|
||||
try {
|
||||
peer.send(stamped);
|
||||
sent++;
|
||||
} catch {
|
||||
room.connections.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`[RELAY] Forwarded to ${sent} peers`);
|
||||
});
|
||||
|
||||
const onClose = () => {
|
||||
console.log(`[DISCONNECT] Peer ${peerId} left room "${roomId}" (${room.connections.size - 1} peers remaining)`);
|
||||
room.connections.delete(peerIdStr);
|
||||
|
||||
// Broadcast LEAVE message to remaining peers
|
||||
const leaveMsg = Buffer.alloc(9);
|
||||
leaveMsg.writeUInt8(2, 0); // MSG_LEAVE
|
||||
leaveMsg.writeUInt32LE(peerId, 1);
|
||||
leaveMsg.writeUInt32LE(0, 5); // sequence 0
|
||||
|
||||
for (const [, peer] of room.connections) {
|
||||
try {
|
||||
peer.send(leaveMsg);
|
||||
} catch {
|
||||
// Ignore send errors on cleanup
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up empty rooms
|
||||
if (room.connections.size === 0) {
|
||||
rooms.delete(pathParts[1]);
|
||||
}
|
||||
};
|
||||
|
||||
ws.on("close", onClose);
|
||||
ws.on("error", onClose);
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Relay server listening on http://localhost:${PORT}`);
|
||||
});
|
||||
12
extensions/src/multiplayer/server/wrangler.toml
Normal file
12
extensions/src/multiplayer/server/wrangler.toml
Normal file
@ -0,0 +1,12 @@
|
||||
name = "isle-relay"
|
||||
main = "relay.ts"
|
||||
compatibility_date = "2024-01-01"
|
||||
|
||||
[durable_objects]
|
||||
bindings = [
|
||||
{ name = "GAME_ROOM", class_name = "GameRoom" }
|
||||
]
|
||||
|
||||
[[migrations]]
|
||||
tag = "v1"
|
||||
new_sqlite_classes = ["GameRoom"]
|
||||
Loading…
Reference in New Issue
Block a user