// 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; }