Skip to content

Commit 213ea9f

Browse files
committed
Cleanup GameServer
1 parent 01e8cd8 commit 213ea9f

5 files changed

Lines changed: 144 additions & 108 deletions

File tree

src/device.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
let _isMobile: boolean | undefined;
1+
export type Role = 'host' | 'client';
2+
const r = new URLSearchParams(location.search).get('role');
3+
const role: Role | undefined = r === 'host' || r === 'client' ? r : undefined;
4+
5+
export const getRole = () => role;
26

3-
const role: string | undefined = new URLSearchParams(location.search).get('role') ?? undefined;
4-
const isClient = role === 'client';
5-
const isHost = role === 'host';
6-
const hasRole = isClient || isHost;
7+
8+
let _isMobile: boolean | undefined;
79

810
export function isMobile(): boolean {
9-
if (_isMobile) return _isMobile;
10-
if (hasRole) {
11-
_isMobile = isClient;
11+
if (_isMobile)
1212
return _isMobile;
13-
}
13+
1414
const ua = navigator.userAgent || '';
1515
const chMobile = (navigator as any).userAgentData?.mobile === true; // Chrome/Edge
1616
const touch = navigator.maxTouchPoints > 0 || matchMedia('(pointer: coarse)').matches;

src/game/game-match.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { Peer, type DataConnection } from 'peerjs';
2+
import type { PlayerAction } from "./player";
3+
import { getRole } from "../device";
4+
5+
export class GameServer {
6+
7+
static readonly baseId = 'lpedwin_winterboard'
8+
static readonly matchId: string = 'xDg5LkoLfF';
9+
static get playerId(): string { return getRole() == 'client' ? 'Player2' : 'Player1'; }
10+
static readonly hostId: string = this.baseId + `_${this.matchId}_Player1`;
11+
static get id() { return this.baseId + `_${this.matchId}_${this.playerId}`; }
12+
private conn: DataConnection | undefined = undefined;
13+
private otherId?: string;
14+
private peer?: Peer;
15+
16+
onAction?: (action: PlayerAction) => void;
17+
18+
19+
static async host(): Promise<GameServer> {
20+
const host = new Peer(this.id);
21+
const game = new GameServer();
22+
game.peer = host;
23+
host.on('open', id => console.log('Host created with id', id));
24+
host.on('error', err => console.error('Host error', err));
25+
26+
host.on('connection', conn => {
27+
conn.on('data', d => game.onAction?.(d as PlayerAction));
28+
conn.on('close', () => {
29+
console.log('Connection closed.');
30+
if (game.conn === conn) game.conn = undefined;
31+
});
32+
conn.on('error', e => console.error('Conn error', e));
33+
34+
conn.on('open', () => {
35+
game.conn = conn;
36+
game.otherId = conn.peer;
37+
conn.send('Current game state');
38+
});
39+
});
40+
41+
return game;
42+
}
43+
44+
static async join(): Promise<GameServer> {
45+
const client = new Peer(this.id);
46+
const game = new GameServer();
47+
game.peer = client;
48+
49+
client.on('open', id => console.log('Peer created with id', id));
50+
client.on('error', err => console.error('Peer error', err));
51+
52+
client.on('open', () => {
53+
const conn = client.connect(this.hostId);
54+
55+
conn.on('data', d => game.onAction?.(d as PlayerAction));
56+
conn.on('close', () => {
57+
console.log('Connection closed.');
58+
if (game.conn === conn) game.conn = undefined;
59+
});
60+
conn.on('error', e => console.error('Conn error', e));
61+
62+
conn.on('open', () => {
63+
console.log('Client connection opened.');
64+
game.conn = conn;
65+
});
66+
});
67+
68+
return game;
69+
}
70+
71+
72+
send(action: PlayerAction) {
73+
if (this.conn)
74+
this.conn.send(action)
75+
else
76+
console.log("No data connection set.");
77+
}
78+
79+
dispose() {
80+
this.conn?.close();
81+
this.peer?.destroy();
82+
}
83+
}

src/game/match.ts

Lines changed: 0 additions & 76 deletions
This file was deleted.

src/game/player.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Vector3 } from "three";
2+
3+
export type Vec3 = [number, number, number]
4+
5+
export const toVec3 = (v: { x: number; y: number; z: number }): Vec3 => [v.x, v.y, v.z];
6+
export const fromVec3 = (v: Vec3) => new Vector3(v[0], v[1], v[2]);
7+
8+
export interface Player {
9+
id?: number;
10+
name?: string;
11+
}
12+
13+
export type ActionType = 'Ability' | 'Attack' | 'Move' | 'None';
14+
15+
export interface PlayerAction {
16+
type: ActionType;
17+
targetPosition?: Vec3;
18+
}

src/main.ts

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { HDRLoader } from "three/examples/jsm/loaders/HDRLoader.js";
55
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
66
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
77
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
8-
import { isMobile } from "./device";
9-
import { Match } from "./game/match";
8+
import { getRole, isMobile } from "./device";
9+
import { GameServer } from "./game/game-match";
10+
import { fromVec3, toVec3, type PlayerAction, type Vec3 } from "./game/player";
1011

1112
// Required for Github Pages deployment
1213
THREE.DefaultLoadingManager.setURLModifier((url) => {
@@ -55,7 +56,7 @@ controls.mouseButtons = {
5556
RIGHT: THREE.MOUSE.ROTATE
5657
};
5758

58-
// Lights
59+
// === Lights ===
5960
const radianceMap = await new HDRLoader().loadAsync('/hdri/kloppenheim_02_puresky_1k.hdr');
6061
scene.environment = (await buildPrefilteredRadianceMap(radianceMap, renderer)).texture;
6162

@@ -65,20 +66,6 @@ dirLight.castShadow = true;
6566
dirLight.shadow.mapSize.set(1024, 1024);
6667
scene.add(dirLight);
6768

68-
69-
// const dirLight = new THREE.DirectionalLight(0xffffff, 1.5);
70-
// dirLight.position.set(-5, 10, -5);
71-
// dirLight.castShadow = true;
72-
// dirLight.shadow.mapSize.set(1024, 1024);
73-
// scene.add(dirLight);
74-
75-
// const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1);
76-
// scene.add(hemiLight);
77-
78-
// const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
79-
// scene.add(ambientLight);
80-
81-
8269
// === Board ===
8370
const boardSize = 8;
8471
const tileSize = 1;
@@ -101,10 +88,11 @@ const oddMat = new THREE.MeshStandardMaterial({
10188

10289
// === Scene setup ===
10390
async function init() {
104-
const game = isMobile() ? await Match.join() : await Match.host();
105-
game.onAction = (data) => { currentTarget = new THREE.Vector3(-4 + Math.random() * 8, currentTarget?.y, -4 + Math.random() * 8) };
106-
window.addEventListener('pagehide', () => game.dispose(), { once: true });
107-
window.addEventListener('beforeunload', () => game.dispose(), { once: true });
91+
const server = await createMatchAsync();
92+
if (server) {
93+
server.onAction = action => playActionLocal(action);
94+
window.addEventListener('pagehide', () => server.dispose(), { once: true });
95+
}
10896

10997
const tileSrc = (await loadGLB("/models/box.glb")).children[0] as THREE.Mesh;
11098
const tileGeometry = (tileSrc.geometry as THREE.BufferGeometry).clone();
@@ -215,12 +203,11 @@ async function init() {
215203
const intersects = raycaster.intersectObjects(tiles, false);
216204

217205
if (intersects.length > 0) {
218-
game.play({ type: 'Move' });
219206
const picked = intersects[0]?.object as THREE.Mesh;
220207
currentTarget = picked.getWorldPosition(new THREE.Vector3());
221208
currentTarget.x += 0.4;
222209
currentTarget.y = scar.position.y;
223-
210+
playAction({ type: 'Move', targetPosition: toVec3(currentTarget) });
224211
if (currentPick) {
225212
// restore material
226213
currentPick.material = currentMaterial!;
@@ -307,6 +294,30 @@ async function init() {
307294
}
308295
return el;
309296
}
297+
298+
async function createMatchAsync(): Promise<GameServer | undefined> {
299+
const role = getRole();
300+
switch (role) {
301+
case 'client': return GameServer.join();
302+
case 'host': return GameServer.host();
303+
case undefined: undefined;
304+
}
305+
}
306+
307+
function playAction(action: PlayerAction) {
308+
server?.send(action);
309+
playActionLocal(action);
310+
}
311+
312+
function playActionLocal(action: PlayerAction) {
313+
switch (action.type) {
314+
case 'Move': currentTarget = mapXZFrom(fromVec3(action.targetPosition!), scar.position);
315+
}
316+
}
317+
318+
function mapXZFrom(source: THREE.Vector3, target: THREE.Vector3) {
319+
return new THREE.Vector3(source.x, target.y, source.z);
320+
}
310321
}
311322

312323
init().catch(console.error);

0 commit comments

Comments
 (0)