cuberef_core 0.0.1

Multiplayer voxel game written in Rust - Core code shared between client and server
Documentation
// Copyright 2023 drey7925
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

syntax = "proto3";

package cuberef.protocol.game_rpc;

import "coordinates.proto";
import "blocks.proto";
import "mapchunk.proto";
import "items.proto";

service CuberefGame {
    // Represents a stream carrying game events.
    // In the future, multiple parallel streams may be used to separate bulk vs low-latency
    // events.
    rpc GameStream(stream StreamToServer) returns (stream StreamToClient);

    // TODO Do not use - needs redesign to use PAKE
    rpc Authenticate(AuthRequest) returns (AuthResponse);

    // Get all the blocks defined in the game.
    rpc GetBlockDefs(GetBlockDefsRequest) returns (GetBlockDefsResponse);

    // Get all the items defined in the game.
    rpc GetItemDefs(GetItemDefsRequest) returns (GetItemDefsResponse);

    // List all media that would be needed by the client
    rpc ListMedia(ListMediaRequest) returns (ListMediaResponse);

    // Fetch media
    rpc GetMedia(GetMediaRequest) returns (GetMediaResponse);
}

message AuthRequest {
    // TODO design for PAKE
}

message AuthResponse {
    bytes token = 1;
}

message GetBlockDefsRequest {}
message GetBlockDefsResponse {
    repeated cuberef.protocol.blocks.BlockTypeDef block_types = 1;
}

message GetItemDefsRequest {}
message GetItemDefsResponse {
    repeated cuberef.protocol.items.ItemDef item_defs = 1;
}

message GetMediaRequest {
    string media_name = 1;
}
message GetMediaResponse {
    bytes media = 1;
}

message ListMediaRequest {}
message ListMediaEntry {
    string media_name = 1;
    bytes sha256 = 2;
}
message ListMediaResponse {
    repeated ListMediaEntry media = 1;
}

message StreamToServer {
    uint64 sequence = 1;
    uint64 client_tick = 2;

    oneof client_message {
        // Keepalive/testing
        Nop nop = 81;
        // Client wants to dig
        DigAction dig = 82;
        // Client is updating realtime position (and also animation state, in the future)
        PositionUpdate position_update = 83;
        // Client wants to tap an item without digging it
        TapAction tap = 84;
        // Client is placing a block
        PlaceAction place = 85;
        // Something went wrong in the client/server state machine and the client detected an inconsistency
        // Send a backtrace and other useful info to the server.
        //
        // e.g., server sent a delta update, but the client didn't have the block in memory
        ClientBugCheck bug_check = 127;
    };
}

message StreamToClient {
    uint64 tick = 1;

    oneof server_message {
        // We've finished handling a request from the server->client stream,
        // and this was the sequence number of it.
        uint64 handled_sequence = 80; 
        // Empty keepalive/testing message
        Nop nop = 81;
        // A block is being changed on a chunk
        MapDeltaUpdateBatch map_delta_update = 82;
        // Server gives client a chunk. Client should cache it, render it if desired,
        // and keep up with map_delta_updates for it
        MapChunk map_chunk = 83;
        // Server will stop sending these chunks. Client may keep them cached if memory
        // is plentiful, but it may also drop them (server will refresh any chunks that were dropped
        // before sending delta updates)
        MapChunkUnsubscribe map_chunk_unsubscribe = 84;
        // An inventory is being updated, and we think the client cares about this inventory.
        // For now, this is only the player's own main inventory.
        InventoryUpdate inventory_update = 85;
        // Client state needs to be set (either during game startup or because the
        // player is being teleported
        SetClientState client_state = 86;
    };
}

// Empty message, for keepalive/timeout detection
message Nop {}

message DigAction {
    // The block coordinate which was dug
    cuberef.protocol.coordinates.BlockCoordinate block_coord = 1;
    // The block coordinate on the face that was dug into (i.e. in raycasting just before we hit the target block)
    cuberef.protocol.coordinates.BlockCoordinate prev_coord = 2;
    // zero-indexed slot for the tool in the player's hotbar/primary inventory
    uint32 item_slot = 3;
}

message TapAction {
    // The block coordinate that was tapped
    cuberef.protocol.coordinates.BlockCoordinate block_coord = 1;
    // The block coordinate on the face that was tapped (i.e. in raycasting just before we hit the target block)
    cuberef.protocol.coordinates.BlockCoordinate prev_coord = 2;
    // zero-indexed slot for the tool in the player's hotbar/primary inventory
    uint32 item_slot = 3;
}

message PlaceAction {
    // The block coordinate where the block is being placed
    cuberef.protocol.coordinates.BlockCoordinate block_coord = 1;

    // The block coordinate onto which the placement is happening (i.e. the block just *after* block_coord in raycasting order)
    cuberef.protocol.coordinates.BlockCoordinate anchor = 2;

    uint32 item_slot = 3;
}

message MapDeltaUpdateBatch {
    repeated MapDeltaUpdate updates = 1;
}

message MapDeltaUpdate {
    cuberef.protocol.coordinates.BlockCoordinate block_coord = 1;
    uint32 new_id = 2;
}


message MapChunk {
    // x/y/z are chunk coordinates
    cuberef.protocol.coordinates.ChunkCoordinate chunk_coord = 1;
    cuberef.protocol.map.StoredChunk chunk_data = 4;
}

message MapChunkUnsubscribe {
    repeated cuberef.protocol.coordinates.ChunkCoordinate chunk_coord = 1;
}

message ClientBugCheck {
    string description = 1;
    string backtrace = 2;
}

message PositionUpdate {
    cuberef.protocol.coordinates.Vec3D position = 1;
    cuberef.protocol.coordinates.Vec3D velocity = 2;
    cuberef.protocol.coordinates.Angles face_direction = 3;
}

message InventoryUpdate {
    cuberef.protocol.items.Inventory inventory = 1;
}

message SetClientState {
    PositionUpdate position = 1;
    bytes main_inventory_id = 2;
}