Compare commits
2 Commits
2c3a1bbdb6
...
990419c351
Author | SHA1 | Date | |
---|---|---|---|
|
990419c351 | ||
|
29befe969d |
19
src/client/client.js
Normal file
19
src/client/client.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
export default class Client {
|
||||||
|
constructor() {
|
||||||
|
this.listeners = [];
|
||||||
|
}
|
||||||
|
accept(data) {
|
||||||
|
for (const i in this.listeners) {
|
||||||
|
this.listeners[i](data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMessageListener(listener) {
|
||||||
|
this.listeners.push(listener);
|
||||||
|
}
|
||||||
|
removeMessageListener(listener) {
|
||||||
|
const index = this.listeners.indexOf(listener);
|
||||||
|
if (-1 !== index) {
|
||||||
|
this.listeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
import Server from './server.js';
|
import Client from './client.js';
|
||||||
|
|
||||||
export default class Local extends Server {
|
export default class LocalClient extends Client {
|
||||||
async connect() {
|
async connect() {
|
||||||
this.worker = new Worker('/server/worker.js', {type: 'module'});
|
this.worker = new Worker('../server/worker.js', {type: 'module'});
|
||||||
this.worker.onmessage = (event) => {
|
this.worker.onmessage = (event) => {
|
||||||
this.accept(event.data);
|
this.accept(event.data);
|
||||||
};
|
};
|
|
@ -1,8 +1,8 @@
|
||||||
import Server from './server.js';
|
import Client from './client.js';
|
||||||
|
|
||||||
export default class Remote extends Server {
|
export default class RemoteClient extends Client {
|
||||||
async connect() {
|
async connect() {
|
||||||
this.socket = new WebSocket('/ws');
|
this.socket = new WebSocket(`ws://${window.location.host}/ws`);
|
||||||
this.socket.onmessage = (event) => {
|
this.socket.onmessage = (event) => {
|
||||||
for (const i in this.listeners) {
|
for (const i in this.listeners) {
|
||||||
this.listeners[i](JSON.parse(event.data));
|
this.listeners[i](JSON.parse(event.data));
|
|
@ -1,9 +1,4 @@
|
||||||
import {
|
import {Sprite} from '@pixi/react';
|
||||||
Sprite,
|
|
||||||
} from '@pixi/react';
|
|
||||||
import {useContext, useEffect, useState} from 'react';
|
|
||||||
|
|
||||||
import ServerContext from '../context/server';
|
|
||||||
|
|
||||||
export default function Entities({entities}) {
|
export default function Entities({entities}) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,12 +5,12 @@ import {
|
||||||
import {useContext, useEffect, useState} from 'react';
|
import {useContext, useEffect, useState} from 'react';
|
||||||
|
|
||||||
import {RESOLUTION} from '../constants.js';
|
import {RESOLUTION} from '../constants.js';
|
||||||
import ServerContext from '../context/server';
|
import ClientContext from '../context/client';
|
||||||
import Entities from './entities';
|
import Entities from './entities';
|
||||||
import styles from './pixi.module.css';
|
import styles from './pixi.module.css';
|
||||||
|
|
||||||
export default function Pixi() {
|
export default function Pixi() {
|
||||||
const server = useContext(ServerContext);
|
const client = useContext(ClientContext);
|
||||||
const [entities, setEntities] = useState({});
|
const [entities, setEntities] = useState({});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onMessage(message) {
|
function onMessage(message) {
|
||||||
|
@ -27,9 +27,9 @@ export default function Pixi() {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server.addMessageListener(onMessage);
|
client.addMessageListener(onMessage);
|
||||||
return () => {
|
return () => {
|
||||||
server.removeMessageListener(onMessage);
|
client.removeMessageListener(onMessage);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,41 +1,41 @@
|
||||||
import {useEffect, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
import ServerContext from '../context/server.js';
|
import ClientContext from '../context/client.js';
|
||||||
import Title from './title';
|
import Title from './title';
|
||||||
import Ui from './ui';
|
import Ui from './ui';
|
||||||
|
|
||||||
export default function Silphius() {
|
export default function Silphius() {
|
||||||
const connectionTuple = useState();
|
const connectionTuple = useState();
|
||||||
const [server, setServer] = useState();
|
const [client, setClient] = useState();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!connectionTuple[0]) {
|
if (!connectionTuple[0]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
async function connect() {
|
async function connect() {
|
||||||
let Server;
|
let Client;
|
||||||
switch (connectionTuple[0]) {
|
switch (connectionTuple[0]) {
|
||||||
case 'local':
|
case 'local':
|
||||||
({default: Server} = await import('../server/local.js'));
|
({default: Client} = await import('../client/local.js'));
|
||||||
break;
|
break;
|
||||||
case 'remote':
|
case 'remote':
|
||||||
({default: Server} = await import('../server/remote.js'));
|
({default: Client} = await import('../client/remote.js'));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const server = new Server();
|
const client = new Client();
|
||||||
await server.connect();
|
await client.connect();
|
||||||
server.send({type: 'connect'});
|
client.send({type: 'connect'});
|
||||||
setServer(server);
|
setClient(client);
|
||||||
}
|
}
|
||||||
connect();
|
connect();
|
||||||
}, [connectionTuple[0]]);
|
}, [connectionTuple[0]]);
|
||||||
return (
|
return (
|
||||||
connectionTuple[0]
|
connectionTuple[0]
|
||||||
? (
|
? (
|
||||||
server
|
client
|
||||||
? (
|
? (
|
||||||
<ServerContext.Provider value={server}>
|
<ClientContext.Provider value={client}>
|
||||||
<Ui />
|
<Ui />
|
||||||
</ServerContext.Provider>
|
</ClientContext.Provider>
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {useContext, useEffect} from 'react';
|
||||||
|
|
||||||
import addKeyListener from '../add-key-listener.js';
|
import addKeyListener from '../add-key-listener.js';
|
||||||
import {RESOLUTION} from '../constants';
|
import {RESOLUTION} from '../constants';
|
||||||
import ServerContext from '../context/server';
|
import ClientContext from '../context/client';
|
||||||
import Dom from './dom';
|
import Dom from './dom';
|
||||||
import Pixi from './pixi';
|
import Pixi from './pixi';
|
||||||
import styles from './ui.module.css';
|
import styles from './ui.module.css';
|
||||||
|
@ -23,11 +23,11 @@ const KEY_MAP = {
|
||||||
|
|
||||||
export default function Ui() {
|
export default function Ui() {
|
||||||
// Key input.
|
// Key input.
|
||||||
const server = useContext(ServerContext);
|
const client = useContext(ClientContext);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return addKeyListener(document.body, ({type, payload}) => {
|
return addKeyListener(document.body, ({type, payload}) => {
|
||||||
if (type in KEY_MAP && payload in ACTION_MAP) {
|
if (type in KEY_MAP && payload in ACTION_MAP) {
|
||||||
server.send({
|
client.send({
|
||||||
type: 'action',
|
type: 'action',
|
||||||
payload: {
|
payload: {
|
||||||
type: ACTION_MAP[payload],
|
type: ACTION_MAP[payload],
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
const SPEED = 100;
|
|
||||||
const TPS = 60;
|
|
||||||
|
|
||||||
const MOVE_MAP = {
|
|
||||||
'moveUp': 0,
|
|
||||||
'moveRight': 1,
|
|
||||||
'moveDown': 2,
|
|
||||||
'moveLeft': 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Engine {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.dude = {
|
|
||||||
image: './assets/bunny.png',
|
|
||||||
movement: [0, 0, 0, 0],
|
|
||||||
position: [50, 50],
|
|
||||||
};
|
|
||||||
this.frame = 0;
|
|
||||||
this.last = Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
accept({type, payload}) {
|
|
||||||
switch (type) {
|
|
||||||
case 'connect': {
|
|
||||||
this.send({type: 'connected', payload: {dude: this.dude}});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'action': {
|
|
||||||
if (payload.type in MOVE_MAP) {
|
|
||||||
this.dude.movement[MOVE_MAP[payload.type]] = payload.value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
send(data) {
|
|
||||||
throw new Exception('Engine::send is virtual!');
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
return setInterval(() => {
|
|
||||||
const elapsed = (Date.now() - this.last) / 1000;
|
|
||||||
this.last = Date.now();
|
|
||||||
this.tick(elapsed);
|
|
||||||
}, 1000 / TPS);
|
|
||||||
}
|
|
||||||
|
|
||||||
tick(elapsed) {
|
|
||||||
this.dude.position[0] += SPEED * elapsed * (this.dude.movement[1] - this.dude.movement[3]);
|
|
||||||
this.dude.position[1] += SPEED * elapsed * (this.dude.movement[2] - this.dude.movement[0]);
|
|
||||||
this.send({
|
|
||||||
type: 'tick',
|
|
||||||
payload: {
|
|
||||||
entities: {dude: this.dude},
|
|
||||||
elapsed,
|
|
||||||
frame: this.frame,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.frame += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +1,61 @@
|
||||||
|
const SPEED = 100;
|
||||||
|
const TPS = 60;
|
||||||
|
|
||||||
|
const MOVE_MAP = {
|
||||||
|
'moveUp': 0,
|
||||||
|
'moveRight': 1,
|
||||||
|
'moveDown': 2,
|
||||||
|
'moveLeft': 3,
|
||||||
|
};
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.listeners = [];
|
this.dude = {
|
||||||
|
image: './assets/bunny.png',
|
||||||
|
movement: [0, 0, 0, 0],
|
||||||
|
position: [50, 50],
|
||||||
|
};
|
||||||
|
this.frame = 0;
|
||||||
|
this.last = Date.now();
|
||||||
}
|
}
|
||||||
accept(data) {
|
|
||||||
for (const i in this.listeners) {
|
accept({type, payload}) {
|
||||||
this.listeners[i](data);
|
switch (type) {
|
||||||
|
case 'connect': {
|
||||||
|
this.send({type: 'connected', payload: {dude: this.dude}});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'action': {
|
||||||
|
if (payload.type in MOVE_MAP) {
|
||||||
|
this.dude.movement[MOVE_MAP[payload.type]] = payload.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addMessageListener(listener) {
|
|
||||||
this.listeners.push(listener);
|
start() {
|
||||||
}
|
return setInterval(() => {
|
||||||
removeMessageListener(listener) {
|
const elapsed = (Date.now() - this.last) / 1000;
|
||||||
const index = this.listeners.indexOf(listener);
|
this.last = Date.now();
|
||||||
if (-1 !== index) {
|
this.tick(elapsed);
|
||||||
this.listeners.splice(index, 1);
|
}, 1000 / TPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tick(elapsed) {
|
||||||
|
this.dude.position[0] += SPEED * elapsed * (this.dude.movement[1] - this.dude.movement[3]);
|
||||||
|
this.dude.position[1] += SPEED * elapsed * (this.dude.movement[2] - this.dude.movement[0]);
|
||||||
|
this.send({
|
||||||
|
type: 'tick',
|
||||||
|
payload: {
|
||||||
|
entities: {dude: this.dude},
|
||||||
|
elapsed,
|
||||||
|
frame: this.frame,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.frame += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {WebSocketServer} from 'ws';
|
import {WebSocketServer} from 'ws';
|
||||||
|
|
||||||
import Engine from './engine.js';
|
import Server from './server.js';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SILPHIUS_WEBSOCKET_PORT = 8080
|
SILPHIUS_WEBSOCKET_PORT = 8080
|
||||||
|
@ -9,7 +9,7 @@ const {
|
||||||
const wss = new WebSocketServer({port: SILPHIUS_WEBSOCKET_PORT});
|
const wss = new WebSocketServer({port: SILPHIUS_WEBSOCKET_PORT});
|
||||||
|
|
||||||
wss.on('connection', function connection(ws) {
|
wss.on('connection', function connection(ws) {
|
||||||
const server = new class WorkerEngine extends Engine {
|
const server = new class SocketServer extends Server {
|
||||||
send(data) { ws.send(JSON.stringify(data)); }
|
send(data) { ws.send(JSON.stringify(data)); }
|
||||||
}
|
}
|
||||||
ws.on('message', function message(data) {
|
ws.on('message', function message(data) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Engine from './engine.js';
|
import Server from './server.js';
|
||||||
|
|
||||||
const server = new class WorkerEngine extends Engine {
|
const server = new class WorkerServer extends Server {
|
||||||
send(data) { postMessage(data); }
|
send(data) { postMessage(data); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user