Compare commits

...

2 Commits

Author SHA1 Message Date
00aa302229 bin 2025-04-16 12:50:46 +02:00
c720b66818 Add timestamp to protobuf and only send unseen messages to clients 2025-04-16 11:49:49 +02:00
4 changed files with 260 additions and 179 deletions

View File

@ -1,8 +1,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.3 // protoc-gen-go v1.36.6
// protoc v5.29.2 // protoc v6.30.1
// source: actions.proto // source: actions/actions.proto
package actions package actions
@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
sync "sync" sync "sync"
unsafe "unsafe"
) )
const ( const (
@ -59,11 +60,11 @@ func (x Action_ActionType) String() string {
} }
func (Action_ActionType) Descriptor() protoreflect.EnumDescriptor { func (Action_ActionType) Descriptor() protoreflect.EnumDescriptor {
return file_actions_proto_enumTypes[0].Descriptor() return file_actions_actions_proto_enumTypes[0].Descriptor()
} }
func (Action_ActionType) Type() protoreflect.EnumType { func (Action_ActionType) Type() protoreflect.EnumType {
return &file_actions_proto_enumTypes[0] return &file_actions_actions_proto_enumTypes[0]
} }
func (x Action_ActionType) Number() protoreflect.EnumNumber { func (x Action_ActionType) Number() protoreflect.EnumNumber {
@ -72,7 +73,7 @@ func (x Action_ActionType) Number() protoreflect.EnumNumber {
// Deprecated: Use Action_ActionType.Descriptor instead. // Deprecated: Use Action_ActionType.Descriptor instead.
func (Action_ActionType) EnumDescriptor() ([]byte, []int) { func (Action_ActionType) EnumDescriptor() ([]byte, []int) {
return file_actions_proto_rawDescGZIP(), []int{0, 0} return file_actions_actions_proto_rawDescGZIP(), []int{0, 0}
} }
type Action struct { type Action struct {
@ -90,7 +91,7 @@ type Action struct {
func (x *Action) Reset() { func (x *Action) Reset() {
*x = Action{} *x = Action{}
mi := &file_actions_proto_msgTypes[0] mi := &file_actions_actions_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -102,7 +103,7 @@ func (x *Action) String() string {
func (*Action) ProtoMessage() {} func (*Action) ProtoMessage() {}
func (x *Action) ProtoReflect() protoreflect.Message { func (x *Action) ProtoReflect() protoreflect.Message {
mi := &file_actions_proto_msgTypes[0] mi := &file_actions_actions_proto_msgTypes[0]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -115,7 +116,7 @@ func (x *Action) ProtoReflect() protoreflect.Message {
// Deprecated: Use Action.ProtoReflect.Descriptor instead. // Deprecated: Use Action.ProtoReflect.Descriptor instead.
func (*Action) Descriptor() ([]byte, []int) { func (*Action) Descriptor() ([]byte, []int) {
return file_actions_proto_rawDescGZIP(), []int{0} return file_actions_actions_proto_rawDescGZIP(), []int{0}
} }
func (x *Action) GetType() Action_ActionType { func (x *Action) GetType() Action_ActionType {
@ -173,13 +174,14 @@ type ActionBatch struct {
Actions []*Action `protobuf:"bytes,2,rep,name=actions,proto3" json:"actions,omitempty"` Actions []*Action `protobuf:"bytes,2,rep,name=actions,proto3" json:"actions,omitempty"`
Tick int64 `protobuf:"varint,3,opt,name=tick,proto3" json:"tick,omitempty"` Tick int64 `protobuf:"varint,3,opt,name=tick,proto3" json:"tick,omitempty"`
ProtocolVersion int32 `protobuf:"varint,4,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"` ProtocolVersion int32 `protobuf:"varint,4,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"`
LastSeenMessageTimestamp int64 `protobuf:"varint,5,opt,name=last_seen_message_timestamp,json=lastSeenMessageTimestamp,proto3" json:"last_seen_message_timestamp,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
func (x *ActionBatch) Reset() { func (x *ActionBatch) Reset() {
*x = ActionBatch{} *x = ActionBatch{}
mi := &file_actions_proto_msgTypes[1] mi := &file_actions_actions_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -191,7 +193,7 @@ func (x *ActionBatch) String() string {
func (*ActionBatch) ProtoMessage() {} func (*ActionBatch) ProtoMessage() {}
func (x *ActionBatch) ProtoReflect() protoreflect.Message { func (x *ActionBatch) ProtoReflect() protoreflect.Message {
mi := &file_actions_proto_msgTypes[1] mi := &file_actions_actions_proto_msgTypes[1]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -204,7 +206,7 @@ func (x *ActionBatch) ProtoReflect() protoreflect.Message {
// Deprecated: Use ActionBatch.ProtoReflect.Descriptor instead. // Deprecated: Use ActionBatch.ProtoReflect.Descriptor instead.
func (*ActionBatch) Descriptor() ([]byte, []int) { func (*ActionBatch) Descriptor() ([]byte, []int) {
return file_actions_proto_rawDescGZIP(), []int{1} return file_actions_actions_proto_rawDescGZIP(), []int{1}
} }
func (x *ActionBatch) GetPlayerId() int32 { func (x *ActionBatch) GetPlayerId() int32 {
@ -235,6 +237,13 @@ func (x *ActionBatch) GetProtocolVersion() int32 {
return 0 return 0
} }
func (x *ActionBatch) GetLastSeenMessageTimestamp() int64 {
if x != nil {
return x.LastSeenMessageTimestamp
}
return 0
}
type PlayerState struct { type PlayerState struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"` PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"`
@ -247,7 +256,7 @@ type PlayerState struct {
func (x *PlayerState) Reset() { func (x *PlayerState) Reset() {
*x = PlayerState{} *x = PlayerState{}
mi := &file_actions_proto_msgTypes[2] mi := &file_actions_actions_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -259,7 +268,7 @@ func (x *PlayerState) String() string {
func (*PlayerState) ProtoMessage() {} func (*PlayerState) ProtoMessage() {}
func (x *PlayerState) ProtoReflect() protoreflect.Message { func (x *PlayerState) ProtoReflect() protoreflect.Message {
mi := &file_actions_proto_msgTypes[2] mi := &file_actions_actions_proto_msgTypes[2]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -272,7 +281,7 @@ func (x *PlayerState) ProtoReflect() protoreflect.Message {
// Deprecated: Use PlayerState.ProtoReflect.Descriptor instead. // Deprecated: Use PlayerState.ProtoReflect.Descriptor instead.
func (*PlayerState) Descriptor() ([]byte, []int) { func (*PlayerState) Descriptor() ([]byte, []int) {
return file_actions_proto_rawDescGZIP(), []int{2} return file_actions_actions_proto_rawDescGZIP(), []int{2}
} }
func (x *PlayerState) GetPlayerId() int32 { func (x *PlayerState) GetPlayerId() int32 {
@ -315,7 +324,7 @@ type ChatMessage struct {
func (x *ChatMessage) Reset() { func (x *ChatMessage) Reset() {
*x = ChatMessage{} *x = ChatMessage{}
mi := &file_actions_proto_msgTypes[3] mi := &file_actions_actions_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -327,7 +336,7 @@ func (x *ChatMessage) String() string {
func (*ChatMessage) ProtoMessage() {} func (*ChatMessage) ProtoMessage() {}
func (x *ChatMessage) ProtoReflect() protoreflect.Message { func (x *ChatMessage) ProtoReflect() protoreflect.Message {
mi := &file_actions_proto_msgTypes[3] mi := &file_actions_actions_proto_msgTypes[3]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -340,7 +349,7 @@ func (x *ChatMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use ChatMessage.ProtoReflect.Descriptor instead. // Deprecated: Use ChatMessage.ProtoReflect.Descriptor instead.
func (*ChatMessage) Descriptor() ([]byte, []int) { func (*ChatMessage) Descriptor() ([]byte, []int) {
return file_actions_proto_rawDescGZIP(), []int{3} return file_actions_actions_proto_rawDescGZIP(), []int{3}
} }
func (x *ChatMessage) GetPlayerId() int32 { func (x *ChatMessage) GetPlayerId() int32 {
@ -386,7 +395,7 @@ type ServerMessage struct {
func (x *ServerMessage) Reset() { func (x *ServerMessage) Reset() {
*x = ServerMessage{} *x = ServerMessage{}
mi := &file_actions_proto_msgTypes[4] mi := &file_actions_actions_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -398,7 +407,7 @@ func (x *ServerMessage) String() string {
func (*ServerMessage) ProtoMessage() {} func (*ServerMessage) ProtoMessage() {}
func (x *ServerMessage) ProtoReflect() protoreflect.Message { func (x *ServerMessage) ProtoReflect() protoreflect.Message {
mi := &file_actions_proto_msgTypes[4] mi := &file_actions_actions_proto_msgTypes[4]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -411,7 +420,7 @@ func (x *ServerMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use ServerMessage.ProtoReflect.Descriptor instead. // Deprecated: Use ServerMessage.ProtoReflect.Descriptor instead.
func (*ServerMessage) Descriptor() ([]byte, []int) { func (*ServerMessage) Descriptor() ([]byte, []int) {
return file_actions_proto_rawDescGZIP(), []int{4} return file_actions_actions_proto_rawDescGZIP(), []int{4}
} }
func (x *ServerMessage) GetPlayerId() int32 { func (x *ServerMessage) GetPlayerId() int32 {
@ -463,92 +472,67 @@ func (x *ServerMessage) GetProtocolVersion() int32 {
return 0 return 0
} }
var File_actions_proto protoreflect.FileDescriptor var File_actions_actions_proto protoreflect.FileDescriptor
var file_actions_proto_rawDesc = []byte{ const file_actions_actions_proto_rawDesc = "" +
0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, "\n" +
0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x97, 0x02, 0x0a, 0x06, 0x41, 0x63, 0x74, "\x15actions/actions.proto\x12\aactions\"\x97\x02\n" +
0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, "\x06Action\x12.\n" +
0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, "\x04type\x18\x01 \x01(\x0e2\x1a.actions.Action.ActionTypeR\x04type\x12\f\n" +
0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, "\x01x\x18\x02 \x01(\x05R\x01x\x12\f\n" +
0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, "\x01y\x18\x03 \x01(\x05R\x01y\x12\x1b\n" +
0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x79, 0x12, "\tplayer_id\x18\x04 \x01(\x05R\bplayerId\x12!\n" +
0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, "\fchat_message\x18\x05 \x01(\tR\vchatMessage\x12\x1a\n" +
0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, "\busername\x18\x06 \x01(\tR\busername\x12\x1a\n" +
0x63, 0x68, 0x61, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, "\bpassword\x18\a \x01(\tR\bpassword\"I\n" +
0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, "\n" +
0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, "ActionType\x12\b\n" +
0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, "\x04MOVE\x10\x00\x12\b\n" +
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, "\x04CHAT\x10\x01\x12\x0e\n" +
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x49, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, "\n" +
0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x00, 0x12, "DISCONNECT\x10\x02\x12\t\n" +
0x08, 0x0a, 0x04, 0x43, 0x48, 0x41, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x49, 0x53, "\x05LOGIN\x10\x03\x12\f\n" +
0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, 0x47, "\bREGISTER\x10\x04\"\xd3\x01\n" +
0x49, 0x4e, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, "\vActionBatch\x12\x1b\n" +
0x10, 0x04, 0x22, 0x94, 0x01, 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x74, "\tplayer_id\x18\x01 \x01(\x05R\bplayerId\x12)\n" +
0x63, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, "\aactions\x18\x02 \x03(\v2\x0f.actions.ActionR\aactions\x12\x12\n" +
0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, "\x04tick\x18\x03 \x01(\x03R\x04tick\x12)\n" +
0x29, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, "\x10protocol_version\x18\x04 \x01(\x05R\x0fprotocolVersion\x12=\n" +
0x32, 0x0f, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, "\x1blast_seen_message_timestamp\x18\x05 \x01(\x03R\x18lastSeenMessageTimestamp\"b\n" +
0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, "\vPlayerState\x12\x1b\n" +
0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x63, 0x6b, 0x12, 0x29, "\tplayer_id\x18\x01 \x01(\x05R\bplayerId\x12\f\n" +
0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, "\x01x\x18\x02 \x01(\x05R\x01x\x12\f\n" +
0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, "\x01y\x18\x03 \x01(\x05R\x01y\x12\x1a\n" +
0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x62, 0x0a, 0x0b, 0x50, 0x6c, 0x61, "\busername\x18\x04 \x01(\tR\busername\"~\n" +
0x79, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, "\vChatMessage\x12\x1b\n" +
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, "\tplayer_id\x18\x01 \x01(\x05R\bplayerId\x12\x1a\n" +
0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, "\busername\x18\x02 \x01(\tR\busername\x12\x18\n" +
0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, "\acontent\x18\x03 \x01(\tR\acontent\x12\x1c\n" +
0x79, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, "\ttimestamp\x18\x04 \x01(\x03R\ttimestamp\"\xad\x02\n" +
0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7e, 0x0a, "\rServerMessage\x12\x1b\n" +
0x0b, 0x43, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, "\tplayer_id\x18\x01 \x01(\x05R\bplayerId\x12.\n" +
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, "\aplayers\x18\x02 \x03(\v2\x14.actions.PlayerStateR\aplayers\x12!\n" +
0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, "\fcurrent_tick\x18\x03 \x01(\x03R\vcurrentTick\x129\n" +
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, "\rchat_messages\x18\x04 \x03(\v2\x14.actions.ChatMessageR\fchatMessages\x12!\n" +
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, "\fauth_success\x18\x05 \x01(\bR\vauthSuccess\x12#\n" +
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, "\rerror_message\x18\x06 \x01(\tR\ferrorMessage\x12)\n" +
0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, "\x10protocol_version\x18\a \x01(\x05R\x0fprotocolVersionB,Z*gitea.boner.be/bdnugget/goonserver/actionsb\x06proto3"
0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xad, 0x02,
0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x07,
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x53, 0x74,
0x61, 0x74, 0x65, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c,
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01,
0x28, 0x03, 0x52, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x12,
0x39, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73,
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x2e, 0x43, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0c, 0x63, 0x68,
0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75,
0x74, 0x68, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a,
0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x2c, 0x5a,
0x2a, 0x67, 0x69, 0x74, 0x65, 0x61, 0x2e, 0x62, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x62, 0x65, 0x2f,
0x62, 0x64, 0x6e, 0x75, 0x67, 0x67, 0x65, 0x74, 0x2f, 0x67, 0x6f, 0x6f, 0x6e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var ( var (
file_actions_proto_rawDescOnce sync.Once file_actions_actions_proto_rawDescOnce sync.Once
file_actions_proto_rawDescData = file_actions_proto_rawDesc file_actions_actions_proto_rawDescData []byte
) )
func file_actions_proto_rawDescGZIP() []byte { func file_actions_actions_proto_rawDescGZIP() []byte {
file_actions_proto_rawDescOnce.Do(func() { file_actions_actions_proto_rawDescOnce.Do(func() {
file_actions_proto_rawDescData = protoimpl.X.CompressGZIP(file_actions_proto_rawDescData) file_actions_actions_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_actions_actions_proto_rawDesc), len(file_actions_actions_proto_rawDesc)))
}) })
return file_actions_proto_rawDescData return file_actions_actions_proto_rawDescData
} }
var file_actions_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_actions_actions_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_actions_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_actions_actions_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_actions_proto_goTypes = []any{ var file_actions_actions_proto_goTypes = []any{
(Action_ActionType)(0), // 0: actions.Action.ActionType (Action_ActionType)(0), // 0: actions.Action.ActionType
(*Action)(nil), // 1: actions.Action (*Action)(nil), // 1: actions.Action
(*ActionBatch)(nil), // 2: actions.ActionBatch (*ActionBatch)(nil), // 2: actions.ActionBatch
@ -556,7 +540,7 @@ var file_actions_proto_goTypes = []any{
(*ChatMessage)(nil), // 4: actions.ChatMessage (*ChatMessage)(nil), // 4: actions.ChatMessage
(*ServerMessage)(nil), // 5: actions.ServerMessage (*ServerMessage)(nil), // 5: actions.ServerMessage
} }
var file_actions_proto_depIdxs = []int32{ var file_actions_actions_proto_depIdxs = []int32{
0, // 0: actions.Action.type:type_name -> actions.Action.ActionType 0, // 0: actions.Action.type:type_name -> actions.Action.ActionType
1, // 1: actions.ActionBatch.actions:type_name -> actions.Action 1, // 1: actions.ActionBatch.actions:type_name -> actions.Action
3, // 2: actions.ServerMessage.players:type_name -> actions.PlayerState 3, // 2: actions.ServerMessage.players:type_name -> actions.PlayerState
@ -568,28 +552,27 @@ var file_actions_proto_depIdxs = []int32{
0, // [0:4] is the sub-list for field type_name 0, // [0:4] is the sub-list for field type_name
} }
func init() { file_actions_proto_init() } func init() { file_actions_actions_proto_init() }
func file_actions_proto_init() { func file_actions_actions_proto_init() {
if File_actions_proto != nil { if File_actions_actions_proto != nil {
return return
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_actions_proto_rawDesc, RawDescriptor: unsafe.Slice(unsafe.StringData(file_actions_actions_proto_rawDesc), len(file_actions_actions_proto_rawDesc)),
NumEnums: 1, NumEnums: 1,
NumMessages: 5, NumMessages: 5,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_actions_proto_goTypes, GoTypes: file_actions_actions_proto_goTypes,
DependencyIndexes: file_actions_proto_depIdxs, DependencyIndexes: file_actions_actions_proto_depIdxs,
EnumInfos: file_actions_proto_enumTypes, EnumInfos: file_actions_actions_proto_enumTypes,
MessageInfos: file_actions_proto_msgTypes, MessageInfos: file_actions_actions_proto_msgTypes,
}.Build() }.Build()
File_actions_proto = out.File File_actions_actions_proto = out.File
file_actions_proto_rawDesc = nil file_actions_actions_proto_goTypes = nil
file_actions_proto_goTypes = nil file_actions_actions_proto_depIdxs = nil
file_actions_proto_depIdxs = nil
} }

View File

@ -27,6 +27,7 @@ message ActionBatch {
repeated Action actions = 2; repeated Action actions = 2;
int64 tick = 3; int64 tick = 3;
int32 protocol_version = 4; int32 protocol_version = 4;
int64 last_seen_message_timestamp = 5;
} }
message PlayerState { message PlayerState {

BIN
goonserver Executable file

Binary file not shown.

197
main.go
View File

@ -27,6 +27,7 @@ type Player struct {
ID int ID int
X, Y int X, Y int
Username string Username string
LastSeenMsgTimestamp int64 // Track the last message timestamp this player has seen
} }
var ( var (
@ -82,7 +83,10 @@ func main() {
} }
func handleConnection(conn net.Conn) { func handleConnection(conn net.Conn) {
defer conn.Close() defer func() {
conn.Close()
log.Printf("Connection closed and cleanup complete")
}()
// Get client IP // Get client IP
remoteAddr := conn.RemoteAddr().String() remoteAddr := conn.RemoteAddr().String()
@ -188,29 +192,57 @@ func handleConnection(conn net.Conn) {
return return
} }
log.Printf("Player %d (%s) authenticated successfully, checking for existing session", playerID, username)
// Check for existing session and force disconnect if needed
mu.Lock()
existingPlayer, alreadyLoggedIn := players[playerID]
if alreadyLoggedIn {
log.Printf("Player %d (%s) is already logged in, forcing disconnect of old session", playerID, username)
// An existing session is found - clean it up
if oldConn, exists := playerConns[playerID]; exists {
// Try to close the old connection
oldConn.Close()
delete(playerConns, playerID)
}
// Keep the player object but update its connection
existingPlayer.X = x
existingPlayer.Y = y
playerConns[playerID] = conn
mu.Unlock()
} else {
// Create a new player
player := &Player{ player := &Player{
ID: playerID, ID: playerID,
X: x, X: x,
Y: y, Y: y,
Username: username, Username: username,
} LastSeenMsgTimestamp: 0, // Initialize to 0 to receive all messages initially
// Prevent multiple logins
mu.Lock()
for _, p := range players {
if p.Username == username {
mu.Unlock()
response := &pb.ServerMessage{
AuthSuccess: false,
ErrorMessage: "Account already logged in",
}
writeMessage(conn, response)
return
}
} }
players[playerID] = player players[playerID] = player
playerConns[playerID] = conn playerConns[playerID] = conn
mu.Unlock() mu.Unlock()
existingPlayer = player
// Announce connection
addSystemMessage(fmt.Sprintf("%s connected", username))
}
// Ensure player state is saved on any kind of disconnect
defer func() {
if p, exists := players[playerID]; exists {
if err := db.SavePlayerState(playerID, p.X, p.Y); err != nil {
log.Printf("Error saving state for player %d: %v", playerID, err)
}
}
addSystemMessage(fmt.Sprintf("%s disconnected", username))
mu.Lock()
delete(players, playerID)
delete(playerConns, playerID)
delete(actionQueue, playerID)
mu.Unlock()
log.Printf("Player %d (%s) disconnected", playerID, username)
}()
// Send initial state with correct position // Send initial state with correct position
response = &pb.ServerMessage{ response = &pb.ServerMessage{
@ -225,29 +257,13 @@ func handleConnection(conn net.Conn) {
ProtocolVersion: protoVersion, ProtocolVersion: protoVersion,
} }
addSystemMessage(fmt.Sprintf("%s connected", username))
// Ensure player state is saved on any kind of disconnect
defer func() {
if err := db.SavePlayerState(playerID, player.X, player.Y); err != nil {
log.Printf("Error saving state for player %d: %v", playerID, err)
}
addSystemMessage(fmt.Sprintf("%s disconnected", player.Username))
mu.Lock()
delete(players, playerID)
delete(playerConns, playerID)
delete(actionQueue, playerID)
mu.Unlock()
log.Printf("Player %d disconnected", playerID)
}()
// Send player ID to client // Send player ID to client
if err := writeMessage(conn, response); err != nil { if err := writeMessage(conn, response); err != nil {
log.Printf("Failed to send player ID: %v", err) log.Printf("Failed to send player ID: %v", err)
return return
} }
fmt.Printf("Player %d connected\n", playerID) log.Printf("Player %d (%s) connected successfully", playerID, username)
// Listen for incoming actions from this player // Listen for incoming actions from this player
for { for {
@ -276,6 +292,11 @@ func handleConnection(conn net.Conn) {
continue continue
} }
// Update the last seen message timestamp
if batch.LastSeenMessageTimestamp > 0 {
existingPlayer.LastSeenMsgTimestamp = batch.LastSeenMessageTimestamp
}
// Queue the actions for processing // Queue the actions for processing
if batch.PlayerId == int32(playerID) { if batch.PlayerId == int32(playerID) {
for _, action := range batch.Actions { for _, action := range batch.Actions {
@ -284,7 +305,9 @@ func handleConnection(conn net.Conn) {
return return
} }
} }
mu.Lock()
actionQueue[playerID] = append(actionQueue[playerID], batch.Actions...) actionQueue[playerID] = append(actionQueue[playerID], batch.Actions...)
mu.Unlock()
} }
} }
} }
@ -330,11 +353,30 @@ func addSystemMessage(content string) {
func processActions() { func processActions() {
mu.Lock() mu.Lock()
defer mu.Unlock() // Make a list of players to process first, to avoid lock contention
activePlayers := make(map[int]*Player)
for id, p := range players {
activePlayers[id] = p
}
activeConns := make(map[int]net.Conn)
for id, conn := range playerConns {
activeConns[id] = conn
}
activeQueues := make(map[int][]*pb.Action)
for id, actions := range actionQueue {
if len(actions) > 0 {
activeQueues[id] = actions
actionQueue[id] = nil // Clear the queue early to avoid double processing
}
}
mu.Unlock()
// Update players based on queued actions // Process actions without holding the global lock
for playerID, actions := range actionQueue { for playerID, actions := range activeQueues {
player := players[playerID] player, exists := activePlayers[playerID]
if !exists {
continue
}
player.Lock() player.Lock()
for _, action := range actions { for _, action := range actions {
switch action.Type { switch action.Type {
@ -348,20 +390,21 @@ func processActions() {
} }
} }
player.Unlock() player.Unlock()
actionQueue[playerID] = nil // Clear the action queue after processing
} }
// Prepare and broadcast the current game state // Prepare current game state
currentTick := time.Now().UnixNano() / int64(tickRate) currentTick := time.Now().UnixNano() / int64(tickRate)
state := &pb.ServerMessage{
CurrentTick: currentTick,
Players: make([]*pb.PlayerState, 0, len(players)),
}
// Convert players to PlayerState // Get recent messages for new connections
for id, p := range players { chatMutex.RLock()
recentMessages := chatHistory[max(0, len(chatHistory)-5):] // Get last 5 for new connections
chatMutex.RUnlock()
// To avoid holding locks too long, prepare player states first
playerStates := make([]*pb.PlayerState, 0, len(activePlayers))
for id, p := range activePlayers {
p.Lock() p.Lock()
state.Players = append(state.Players, &pb.PlayerState{ playerStates = append(playerStates, &pb.PlayerState{
PlayerId: int32(id), PlayerId: int32(id),
X: int32(p.X), X: int32(p.X),
Y: int32(p.Y), Y: int32(p.Y),
@ -370,15 +413,69 @@ func processActions() {
p.Unlock() p.Unlock()
} }
// Add chat messages to the state // Now send updates to each player
for playerID, conn := range activeConns {
player, exists := activePlayers[playerID]
if !exists {
continue
}
state := &pb.ServerMessage{
CurrentTick: currentTick,
Players: playerStates,
}
// Add chat messages - only send those the player hasn't seen
player.Lock()
lastSeen := player.LastSeenMsgTimestamp
player.Unlock()
chatMutex.RLock() chatMutex.RLock()
state.ChatMessages = chatHistory[max(0, len(chatHistory)-5):] // Only send last 5 messages var newMessages []*pb.ChatMessage
// For new connections, send the 5 most recent messages
if lastSeen == 0 && len(recentMessages) > 0 {
newMessages = recentMessages
if len(newMessages) > 0 {
// Update the player's timestamp to the latest message
player.Lock()
player.LastSeenMsgTimestamp = newMessages[len(newMessages)-1].Timestamp
player.Unlock()
}
} else {
// For existing connections, only send new messages
for _, msg := range chatHistory {
if msg.Timestamp > lastSeen {
newMessages = append(newMessages, msg)
}
}
// Update the player's timestamp if we sent them new messages
if len(newMessages) > 0 {
player.Lock()
player.LastSeenMsgTimestamp = newMessages[len(newMessages)-1].Timestamp
player.Unlock()
}
}
state.ChatMessages = newMessages
chatMutex.RUnlock() chatMutex.RUnlock()
// Send to each connected player // Log the number of messages we're sending
for _, conn := range playerConns { if len(newMessages) > 0 {
log.Printf("Sending %d new messages to player %d", len(newMessages), playerID)
}
// Send the state to the player - do this without holding any locks
if err := writeMessage(conn, state); err != nil { if err := writeMessage(conn, state); err != nil {
log.Printf("Failed to send update: %v", err) log.Printf("Failed to send update to player %d: %v", playerID, err)
// Handle connection errors by removing the player
mu.Lock()
delete(players, playerID)
delete(playerConns, playerID)
delete(actionQueue, playerID)
mu.Unlock()
} }
} }
} }