You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

游戏引擎设计咨询:客户端与服务器间动作的清晰流转实现

Great question—building a clean action flow for a multiplayer game is critical to keeping your codebase maintainable, preventing desync bugs, and making it easy to add new gameplay features later. Let’s walk through a structured, battle-tested approach tailored to your setup with the Action interface and centralized server-side Context.

Core Guiding Principles

First, let’s anchor the flow with a few key principles to avoid common pitfalls:

  • Server is the source of truth: Never trust client-side state modifications—all actions must be validated and executed on the server first.
  • Single-responsibility Actions: Each Action implementation should do one specific thing (e.g., MoveEntityAction only handles position updates) to keep logic clean.
  • Idempotency: Ensure executing the same action multiple times doesn’t break state (critical for handling network retries).
  • Optimistic local updates (optional): For responsiveness, clients can pre-apply actions locally, but always roll back if the server rejects them.
Step-by-Step Action Flow Mechanism

1. Client Initiates an Action

When a player triggers an action (e.g., clicking to move, typing a chat message):

  • The client creates an instance of the corresponding Action class (e.g., new MoveEntityAction(playerId, targetX, targetY)).
  • Local pre-validation (optional but recommended): Do quick checks to avoid sending invalid actions (e.g., "is the target position within the map bounds?"). This reduces unnecessary network traffic.
  • Serialize the action (use Protobuf, JSON, or a custom binary format—Protobuf is ideal for performance) and send it to the server, along with a unique requestId (for idempotency and matching responses).

2. Server Receives & Validates the Action

The server’s network layer deserializes the action first, then runs critical validation:

  • Authentication: Verify the client has permission to perform this action (e.g., is the player the owner of the entity being moved?).
  • Rule compliance: Check if the action adheres to game rules (e.g., "is the player not stunned?" "is the target position unobstructed?").
  • Idempotency check: Look up the requestId in a short-term cache—if it’s already been processed, skip execution and send a duplicate response.

If validation fails, send a clear error response to the client (e.g., ActionError.PERMISSION_DENIED or ActionError.INVALID_TARGET).

3. Server Executes the Action

Use a handler registry pattern to decouple action execution from core server logic—this makes adding new Action types trivial without modifying existing code:

  • Define an ActionHandler interface for each action type:
    public interface ActionHandler<T extends Action> {
        void execute(T action, GlobalContext serverContext, ClientConnection sender);
    }
    
  • Create a registry to map Action classes to their handlers:
    public class ActionHandlerRegistry {
        private final Map<Class<? extends Action>, ActionHandler<?>> handlers = new HashMap<>();
    
        public <T extends Action> void register(Class<T> actionClass, ActionHandler<T> handler) {
            handlers.put(actionClass, handler);
        }
    
        @SuppressWarnings("unchecked")
        public <T extends Action> void process(T action, GlobalContext context, ClientConnection sender) {
            ActionHandler<T> handler = (ActionHandler<T>) handlers.get(action.getClass());
            if (handler != null) {
                handler.execute(action, context, sender);
            } else {
                // Log unknown action and send error to client
            }
        }
    }
    
  • For example, the MoveEntityActionHandler would modify the GlobalContext to update the entity’s position, then trigger state synchronization.

4. Server Synchronizes State Changes

After executing the action, the server needs to sync the updated state to relevant clients (not all—since each client only maintains a subset):

  • Sender client: Send an ActionConfirmation with the requestId and any updated state (e.g., the entity’s new position). This confirms the action was successful and lets the client update its local context.
  • Other relevant clients: Send a minimal StateUpdateEvent (not the full Action object) containing only the data they need (e.g., "Entity 123 moved to (45, 67)"). This reduces bandwidth usage.
  • Use delta sync instead of full state dumps: Only send changes from the previous state, not the entire context subset.

5. Client Processes Server Responses

  • On confirmation: Update the local Context subset with the server’s state. If you used optimistic local updates, this confirms the change—no rollback needed.
  • On error: Roll back any optimistic local changes and display a user-friendly message (e.g., "You can’t move there!").
  • On state update from other clients: Directly update the relevant parts of the local context to reflect the game world’s true state.
Additional Optimizations
  • Action throttling: For frequent actions (e.g., continuous movement), throttle client sends to 10-20 times per second instead of sending every frame.
  • Batch processing: Group small, related actions (e.g., multiple chat messages) into a single network packet to reduce overhead.
  • Context versioning: Add a version number to the server’s GlobalContext. Clients can track this version to detect stale state and request a delta sync if needed.
  • Offline action queuing: If a client loses connection, queue actions locally and resend them once reconnected (with duplicate checks on the server).

内容的提问来源于stack exchange,提问作者mrak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:40:19