syntax = "proto3";
option go_package = "."; // "./" corresponds to the "--go_out" value in "protoc" command 

package treasurehunterx;

message Direction {
  int32 dx = 1;
  int32 dy = 2;
}

message Vec2D {
  double x = 1;
  double y = 2;
}

message Polygon2D {
  Vec2D Anchor = 1;
  repeated Vec2D Points = 2;
}

message Vec2DList {
  repeated Vec2D vec2DList = 1;
}

message Polygon2DList {
  repeated Polygon2D polygon2DList = 1;
}

message BattleColliderInfo {
  string stageName = 1;
  map<string, Vec2DList> strToVec2DListMap = 2; 
  map<string, Polygon2DList> strToPolygon2DListMap = 3; 
  int32 stageDiscreteW = 4;
  int32 stageDiscreteH = 5;
  int32 stageTileW = 6;
  int32 stageTileH = 7;

  int32 intervalToPing        = 8;
  int32 willKickIfInactiveFor = 9;
  int32 boundRoomId           = 10;
  int64 battleDurationNanos   = 11;
  int32 serverFps             = 12;
  int32 inputDelayFrames      = 13;
  uint32 inputScaleFrames     = 14;
  int32 nstDelayFrames        = 15;
  int32 inputFrameUpsyncDelayTolerance = 16;  
  int32 maxChasingRenderFramesPerUpdate = 17;
  int32 playerBattleState = 18;
  double rollbackEstimatedDt = 19;
  double rollbackEstimatedDtMillis = 20;
  int64 rollbackEstimatedDtNanos = 21;
}

message Player {
  int32 id = 1;
  double x = 2; 
  double y = 3; 
  Direction dir = 4;
  double speed = 5;
  int32 battleState = 6;
  int32 lastMoveGmtMillis = 7;
  int32 score = 10;
  bool removed = 11;
  int32 joinIndex = 12;
}

message PlayerMeta {
  int32 id = 1;
  string name = 2;
  string displayName = 3;
  string avatar = 4;
  int32 joinIndex = 5;
}

message InputFrameUpsync {
  int32 inputFrameId = 1;
  int32 encodedDir = 6;
}

message InputFrameDownsync {
  int32 inputFrameId = 1;
  repeated uint64 inputList = 2; // Indexed by "joinIndex", we try to compress the "single player input" into 1 word (64-bit for 64-bit Golang runtime) because atomic compare-and-swap only works on 1 word. Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. 
  uint64 confirmedList = 3; // Indexed by "joinIndex", same compression concern as above
}

message HeartbeatUpsync {
  int64 clientTimestamp = 1;     
}

message RoomDownsyncFrame {
  int32 id = 1;
  map<int32, Player> players = 2;
  int64 countdownNanos = 3;
  map<int32, PlayerMeta> playerMetas = 4;
}

message WsReq {
  int32 msgId = 1;
  int32 playerId = 2;
  int32 act = 3;    
  int32 joinIndex = 4;
  int32 ackingFrameId = 5;
  int32 ackingInputFrameId = 6;
  repeated InputFrameUpsync inputFrameUpsyncBatch = 7; 
  HeartbeatUpsync hb = 8; 
}

message WsResp {
  int32 ret = 1;
  int32 echoedMsgId = 2;    
  int32 act = 3; 
  RoomDownsyncFrame rdf = 4; 
  repeated InputFrameDownsync inputFrameDownsyncBatch = 5;
  BattleColliderInfo bciFrame = 6; 
}
