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