De nombreux développeurs commencent à développer un serveur en ligne multi-utilisateurs basé sur la bibliothèque socket.io . Cette bibliothèque rend très simple la mise en œuvre de l'échange de données entre le client et le serveur en temps réel, mais vous devez encore réfléchir et implémenter toute la logique et l'interface de l'interaction serveur-client, ainsi que l'architecture de mise à l'échelle et d'optimisation du trafic.
Je veux parler de la bibliothèque magx , à l'aide de laquelle vous ne pouvez pas penser au composant réseau (communication entre le serveur et les clients), mais concentrez-vous immédiatement sur le développement de la logique du jeu et de l'interface utilisateur du client.
Lors de la conception de l'architecture d'un jeu multijoueur, 2 approches sont généralement envisagées: avec un serveur autoritaire et un non autoritaire (client autoritaire). Ces deux approches sont prises en charge par la bibliothèque magx. Commençons par une approche plus simple - non autoritaire.
Serveur ne faisant pas autorité
Son essence est que le serveur ne contrôle pas les résultats d'entrée de chaque joueur. Les clients suivent indépendamment les actions entrées par le joueur et la logique du jeu localement, après quoi ils envoient le résultat d'une certaine action au serveur. Après cela, le serveur synchronise toutes les actions effectuées avec l'état de jeu des autres clients.
Il est plus facile à mettre en œuvre d'un point de vue architectural, car le serveur est uniquement responsable de la communication entre les clients, sans effectuer de calculs supplémentaires que les clients font.
En utilisant la bibliothèque magx, un tel serveur peut être implémenté en seulement quelques lignes de code:
import * as http from "http"
import { Server, RelayRoom } from "magx"
const server = http.createServer()
const magx = new Server(server)
magx.define("relay", RelayRoom)
// start server
const port = process.env.PORT || 3001
server.listen(port, () => {
console.info(`Server started on http://localhost:${port}`)
})
Maintenant, pour connecter les clients à ce serveur et démarrer leur interaction, installez simplement la bibliothèque js:
npm install --save magx-client
et connectez-le au projet:
import { Client } from "magx-client"
Vous pouvez également utiliser un lien direct pour une utilisation HTML:
<script src="https://cdn.jsdelivr.net/npm/magx-client@0.7.1/dist/magx.js"></script>
Après la connexion, quelques lignes vous permettront de configurer la connexion et l'interaction avec le serveur:
// authenticate to server
await client.authenticate()
// create or join room
const rooms = await client.getRooms("relay")
room = rooms.length
? await client.joinRoom(rooms[0].id)
: await client.createRoom("relay")
console.log("you joined room", name)
// handle state patches
room.onPatch((patch) => updateState(patch))
// handle state snapshot
room.onSnapshot((snapshot) => setState(snapshot))
// handle joined players
room.onMessage("player_join", (id) => console.log("player join", id))
// handle left players
room.onMessage("player_leave", (id) => console.log("player leave", id))
Un exemple détaillé de la façon de créer une interaction entre des clients et un serveur ne faisant pas autorité est décrit dans l'exemple correspondant du projet magx-examples .
Serveur faisant autorité
, , - .
- . , , , , .
, . , (cheating).
. magx , (worker). , .
/ . — . Mosx — , magx - , .
, . mosx — @mx.Object, , @mx. :
@mx.Object
export class Player {
@mx public x = Math.floor(Math.random() * 400)
@mx public y = Math.floor(Math.random() * 400)
}
@mx.Object
export class State {
@mx public players = new Map<string, Player>()
public createPlayer(id: string) {
this.players.set(id, new Player())
}
public removePlayer(id: string) {
this.players.delete(id)
}
public movePlayer(id: string, movement: any) {
const player = this.players.get(id)
if (!player) { return }
player.x += movement.x ? movement.x * 10 : 0
player.y += movement.y ? movement.y * 10 : 0
}
}
, — Map() . (Array) (number, string, boolean) .
. :
export class MosxStateRoom extends Room<State> {
public createState(): any {
// create state
return new State()
}
public createPatchTracker(state: State) {
// create state change tracker
return Mosx.createTracker(state)
}
public onCreate(params: any) {
console.log("MosxStateRoom created!", params)
}
public onMessage(client: Client, type: string, data: any) {
if (type === "move") {
console.log(`MosxStateRoom received message from ${client.id}`, data)
this.state.movePlayer(client.id, data)
}
}
public onJoin(client: Client, params: any) {
console.log(`Player ${client.id} joined MosxStateRoom`, params)
client.send("hello", "world")
this.state.createPlayer(client.id)
}
public onLeave(client: Client) {
this.state.removePlayer(client.id)
}
public onClose() {
console.log("MosxStateRoom closed!")
}
}
— .
const magx = new Server(server, params)
magx.define("mosx-state", MosxStateRoom)
?
:
Mosx
Magx
- API.
- :
- ( webockets)
- ( )
- ( )
- ( )
- Salles intégrées: hall et relais (pour serveur non faisant autorité)
- Bibliothèque client JS Magx pour travailler avec le serveur
- Console de surveillance magx-monitor pour la gestion des salles serveurs, leurs clients et l'affichage de l'état
- Prise en charge complète de Typescript
- Dépendances minimales (pour la bibliothèque notepack.io - pour réduire le trafic réseau)
Ce projet est assez jeune et j'espère que l'attention de la communauté l'aidera à se développer beaucoup plus rapidement.