Skip to content

Server side

SFilinsky edited this page Oct 24, 2021 · 29 revisions

World layer

World layer contains game world state as single data structure. Its responsibility is to process and update it according to events that come from Event layer.

After World layer processed all events, world state snapshot goes to Presentational layer.

Except event processing, World layer can have recurring tasks to update world state with time. Those tasks can run and stop depending on world state.

This layer can be local for single-player games and remote for multiplayer.

World layer passes only updates to presentation layer, so it compares new state value after applying all changes with old state.

Domains

  • Scene entities organization

World layer organized game entities by Entity-Component-System pattern

  • Running NullEngine

NullEngine is used to run physics, detect collisions on server-side

? It might not be needed only for physics because physics can be used directly from Cannon.js

  • World persistence

ECS makes it easy to maintain world persistence.

  • Server-side aspects
    • AI
    • Physics
    • User Input reactions

Architecture

World layer keeps world state in a single object. Object consists of Entities, each entity is a set of Components handled by Systems.

Systems perform changes of world state. They are pure functions, that take world state and any needed extra data, and returns list of updated components. Those functions can be closured by container functions to provide additional data or perform side effects.

Example

// Container function is impure and can 
function bulidDamageSystem(

    // Additional system configuration can be passed to build function
    additionalConfig: { enableLogging?: boolean }

) {

    // Creating local cache
    let cache = {};

    // Create systemImplementationFunction that will be used by WorldLayer
    return (requestedCompoents, relatedEvents) => {
        const result = damageSystemImpl(
            { cache }, 
            requestedCompoents, 
            relatedEvents
        );
        
        // Updating cache if changed
        if (updatedAdditionalArguments?.cache) {
            cache = updatedAdditionalArguments.cache;
        }

        return result.updatedComponents;
    }
}

// Implementation function is pure and only depends on it's arguments
function damageSystemImpl(
    
    // System implementation can have additional arguments or settings
    customArguments: {
        cache: any
    }
    
    // Additional engine parametes
    engineParameters: {
        engineTime: number,
    }

    // Components are provided by World layer depending on what components are listed
    // in system dependency list
    requestedComponents: { 
        health: Component[],
        weapon: Component[] 
    },

    // Events are provided by World layer depending on what event types system subscribed to
    relatedEvents: Event[] 
) {

    const attackEvents = relatedEvents.filter(event => event.type === ATTACK_EVENT_TYPE);

    // Handle all attack events
    ...

    return {
        updatedComponents,
        updatedAddutionalArguments
    }
}