Merge pull request 'feature/db' (#2) from feature/db into master

Reviewed-on: #2
This commit is contained in:
bdnugget 2025-01-19 21:07:47 +00:00
commit e3c570349c
7 changed files with 541 additions and 87 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.db

View File

@ -26,6 +26,8 @@ const (
Action_MOVE Action_ActionType = 0
Action_CHAT Action_ActionType = 1
Action_DISCONNECT Action_ActionType = 2
Action_LOGIN Action_ActionType = 3
Action_REGISTER Action_ActionType = 4
)
// Enum value maps for Action_ActionType.
@ -34,11 +36,15 @@ var (
0: "MOVE",
1: "CHAT",
2: "DISCONNECT",
3: "LOGIN",
4: "REGISTER",
}
Action_ActionType_value = map[string]int32{
"MOVE": 0,
"CHAT": 1,
"DISCONNECT": 2,
"LOGIN": 3,
"REGISTER": 4,
}
)
@ -76,6 +82,8 @@ type Action struct {
Y int32 `protobuf:"varint,3,opt,name=y,proto3" json:"y,omitempty"`
PlayerId int32 `protobuf:"varint,4,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"`
ChatMessage string `protobuf:"bytes,5,opt,name=chat_message,json=chatMessage,proto3" json:"chat_message,omitempty"`
Username string `protobuf:"bytes,6,opt,name=username,proto3" json:"username,omitempty"`
Password string `protobuf:"bytes,7,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -145,13 +153,28 @@ func (x *Action) GetChatMessage() string {
return ""
}
func (x *Action) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *Action) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
type ActionBatch struct {
state protoimpl.MessageState `protogen:"open.v1"`
PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,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"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,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"`
ProtocolVersion int32 `protobuf:"varint,4,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ActionBatch) Reset() {
@ -205,11 +228,19 @@ func (x *ActionBatch) GetTick() int64 {
return 0
}
func (x *ActionBatch) GetProtocolVersion() int32 {
if x != nil {
return x.ProtocolVersion
}
return 0
}
type PlayerState struct {
state protoimpl.MessageState `protogen:"open.v1"`
PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"`
X int32 `protobuf:"varint,2,opt,name=x,proto3" json:"x,omitempty"`
Y int32 `protobuf:"varint,3,opt,name=y,proto3" json:"y,omitempty"`
Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -265,11 +296,19 @@ func (x *PlayerState) GetY() int32 {
return 0
}
func (x *PlayerState) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
type ChatMessage struct {
state protoimpl.MessageState `protogen:"open.v1"`
PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"`
Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"`
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"`
Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -311,6 +350,13 @@ func (x *ChatMessage) GetPlayerId() int32 {
return 0
}
func (x *ChatMessage) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *ChatMessage) GetContent() string {
if x != nil {
return x.Content
@ -326,13 +372,16 @@ func (x *ChatMessage) GetTimestamp() int64 {
}
type ServerMessage struct {
state protoimpl.MessageState `protogen:"open.v1"`
PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"`
Players []*PlayerState `protobuf:"bytes,2,rep,name=players,proto3" json:"players,omitempty"`
CurrentTick int64 `protobuf:"varint,3,opt,name=current_tick,json=currentTick,proto3" json:"current_tick,omitempty"`
ChatMessages []*ChatMessage `protobuf:"bytes,4,rep,name=chat_messages,json=chatMessages,proto3" json:"chat_messages,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
PlayerId int32 `protobuf:"varint,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"`
Players []*PlayerState `protobuf:"bytes,2,rep,name=players,proto3" json:"players,omitempty"`
CurrentTick int64 `protobuf:"varint,3,opt,name=current_tick,json=currentTick,proto3" json:"current_tick,omitempty"`
ChatMessages []*ChatMessage `protobuf:"bytes,4,rep,name=chat_messages,json=chatMessages,proto3" json:"chat_messages,omitempty"`
AuthSuccess bool `protobuf:"varint,5,opt,name=auth_success,json=authSuccess,proto3" json:"auth_success,omitempty"`
ErrorMessage string `protobuf:"bytes,6,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
ProtocolVersion int32 `protobuf:"varint,7,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ServerMessage) Reset() {
@ -393,11 +442,32 @@ func (x *ServerMessage) GetChatMessages() []*ChatMessage {
return nil
}
func (x *ServerMessage) GetAuthSuccess() bool {
if x != nil {
return x.AuthSuccess
}
return false
}
func (x *ServerMessage) GetErrorMessage() string {
if x != nil {
return x.ErrorMessage
}
return ""
}
func (x *ServerMessage) GetProtocolVersion() int32 {
if x != nil {
return x.ProtocolVersion
}
return 0
}
var File_actions_proto protoreflect.FileDescriptor
var file_actions_proto_rawDesc = []byte{
0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xc6, 0x01, 0x0a, 0x06, 0x41, 0x63, 0x74,
0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x97, 0x02, 0x0a, 0x06, 0x41, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
@ -406,43 +476,62 @@ var file_actions_proto_rawDesc = []byte{
0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01,
0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c,
0x63, 0x68, 0x61, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
0x30, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a,
0x04, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x48, 0x41, 0x54, 0x10,
0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10,
0x02, 0x22, 0x69, 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68,
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, 0x29, 0x0a,
0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f,
0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x63, 0x6b,
0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x63, 0x6b, 0x22, 0x46, 0x0a, 0x0b,
0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 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, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20,
0x01, 0x28, 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
0x05, 0x52, 0x01, 0x79, 0x22, 0x62, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 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, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xba, 0x01, 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, 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,
0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x49, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x00, 0x12,
0x08, 0x0a, 0x04, 0x43, 0x48, 0x41, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x49, 0x53,
0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, 0x47,
0x49, 0x4e, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52,
0x10, 0x04, 0x22, 0x94, 0x01, 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x74,
0x63, 0x68, 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,
0x29, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x0f, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69,
0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x63, 0x6b, 0x12, 0x29,
0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x62, 0x0a, 0x0b, 0x50, 0x6c, 0x61,
0x79, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 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, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01,
0x79, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7e, 0x0a,
0x0b, 0x43, 0x68, 0x61, 0x74, 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, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65,
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65,
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12,
0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01,
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 (

View File

@ -9,6 +9,8 @@ message Action {
MOVE = 0;
CHAT = 1;
DISCONNECT = 2;
LOGIN = 3;
REGISTER = 4;
}
ActionType type = 1;
@ -16,24 +18,29 @@ message Action {
int32 y = 3;
int32 player_id = 4;
string chat_message = 5;
string username = 6;
string password = 7;
}
message ActionBatch {
int32 player_id = 1;
repeated Action actions = 2;
int64 tick = 3;
int32 protocol_version = 4;
}
message PlayerState {
int32 player_id = 1;
int32 x = 2;
int32 y = 3;
string username = 4;
}
message ChatMessage {
int32 player_id = 1;
string content = 2;
int64 timestamp = 3;
string username = 2;
string content = 3;
int64 timestamp = 4;
}
message ServerMessage {
@ -41,4 +48,7 @@ message ServerMessage {
repeated PlayerState players = 2;
int64 current_tick = 3;
repeated ChatMessage chat_messages = 4;
bool auth_success = 5;
string error_message = 6;
int32 protocol_version = 7;
}

188
db/db.go Normal file
View File

@ -0,0 +1,188 @@
package db
import (
"crypto/sha256"
"database/sql"
"encoding/hex"
"errors"
"fmt"
"sync"
"time"
_ "github.com/mattn/go-sqlite3"
)
var db *sql.DB
var (
ErrUserExists = errors.New("username already exists")
ErrInvalidCredentials = errors.New("invalid username or password")
)
const (
maxRegistrationsPerIP = 3 // Maximum registrations allowed per IP
registrationWindow = 24 * time.Hour // Time window for rate limiting
)
type registrationAttempt struct {
count int
firstTry time.Time
}
var (
registrationAttempts = make(map[string]*registrationAttempt)
rateLimitMutex sync.RWMutex
)
func CleanupOldAttempts() {
rateLimitMutex.Lock()
defer rateLimitMutex.Unlock()
now := time.Now()
for ip, attempt := range registrationAttempts {
if now.Sub(attempt.firstTry) > registrationWindow {
delete(registrationAttempts, ip)
}
}
}
func CheckRegistrationLimit(ip string) error {
rateLimitMutex.Lock()
defer rateLimitMutex.Unlock()
now := time.Now()
attempt, exists := registrationAttempts[ip]
if !exists {
registrationAttempts[ip] = &registrationAttempt{
count: 1,
firstTry: now,
}
return nil
}
// Reset if window has passed
if now.Sub(attempt.firstTry) > registrationWindow {
attempt.count = 1
attempt.firstTry = now
return nil
}
if attempt.count >= maxRegistrationsPerIP {
return fmt.Errorf("registration limit reached for this IP. Please try again in %v",
registrationWindow-now.Sub(attempt.firstTry))
}
attempt.count++
return nil
}
func InitDB(dbPath string) error {
var err error
db, err = sql.Open("sqlite3", dbPath)
if err != nil {
return err
}
// Create tables if they don't exist
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at DATETIME NOT NULL
);
CREATE TABLE IF NOT EXISTS player_states (
player_id INTEGER PRIMARY KEY,
x INTEGER NOT NULL,
y INTEGER NOT NULL,
last_seen DATETIME NOT NULL,
FOREIGN KEY(player_id) REFERENCES players(id)
);
`)
return err
}
func hashPassword(password string) string {
hash := sha256.Sum256([]byte(password))
return hex.EncodeToString(hash[:])
}
func RegisterPlayer(username, password string) (int, error) {
// Check if username exists
var exists bool
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM players WHERE username = ?)", username).Scan(&exists)
if err != nil {
return 0, err
}
if exists {
return 0, ErrUserExists
}
// Create new player
result, err := db.Exec(`
INSERT INTO players (username, password_hash, created_at)
VALUES (?, ?, ?)`,
username, hashPassword(password), time.Now().UTC(),
)
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
return int(id), err
}
func AuthenticatePlayer(username, password string) (int, error) {
var id int
var storedHash string
err := db.QueryRow(`
SELECT id, password_hash
FROM players
WHERE username = ?`,
username,
).Scan(&id, &storedHash)
if err == sql.ErrNoRows {
return 0, ErrInvalidCredentials
}
if err != nil {
return 0, err
}
if storedHash != hashPassword(password) {
return 0, ErrInvalidCredentials
}
return id, nil
}
func SavePlayerState(playerID int, x, y int) error {
_, err := db.Exec(`
INSERT OR REPLACE INTO player_states (
player_id, x, y, last_seen
) VALUES (?, ?, ?, ?)`,
playerID, x, y, time.Now().UTC(),
)
return err
}
func LoadPlayerState(playerID int) (x, y int, err error) {
err = db.QueryRow(`
SELECT x, y FROM player_states
WHERE player_id = ?`,
playerID,
).Scan(&x, &y)
if err == sql.ErrNoRows {
// Return default position for new players
return 5, 5, nil
}
return x, y, err
}
func GetUsername(playerID int) (string, error) {
var username string
err := db.QueryRow("SELECT username FROM players WHERE id = ?", playerID).Scan(&username)
return username, err
}

5
go.mod
View File

@ -2,4 +2,7 @@ module gitea.boner.be/bdnugget/goonserver
go 1.23.0
require google.golang.org/protobuf v1.36.3
require (
github.com/mattn/go-sqlite3 v1.14.24
google.golang.org/protobuf v1.36.3
)

2
go.sum
View File

@ -1,5 +1,7 @@
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=

223
main.go
View File

@ -11,19 +11,22 @@ import (
"time"
pb "gitea.boner.be/bdnugget/goonserver/actions"
"gitea.boner.be/bdnugget/goonserver/db"
"google.golang.org/protobuf/proto"
)
const (
port = ":6969" // Port to listen on
tickRate = 600 * time.Millisecond
port = ":6969" // Port to listen on
tickRate = 600 * time.Millisecond
protoVersion = 1
)
type Player struct {
sync.Mutex
ID int
X, Y int // Position on the game grid
ID int
X, Y int
Username string
}
var (
@ -36,6 +39,10 @@ var (
)
func main() {
if err := db.InitDB("goonserver.db"); err != nil {
log.Fatalf("Failed to initialize database: %v", err)
}
ln, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("Failed to listen on port %s: %v", port, err)
@ -47,6 +54,15 @@ func main() {
ticker := time.NewTicker(tickRate)
defer ticker.Stop()
// Start registration attempt cleanup goroutine
go func() {
ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
for range ticker.C {
db.CleanupOldAttempts()
}
}()
// Handle incoming connections in a separate goroutine
go func() {
for {
@ -68,34 +84,178 @@ func main() {
func handleConnection(conn net.Conn) {
defer conn.Close()
mu.Lock()
playerID := len(players) + 1
newPlayer := &Player{ID: playerID, X: 5, Y: 5}
players[playerID] = newPlayer
playerConns[playerID] = conn
mu.Unlock()
fmt.Printf("Player %d connected\n", playerID)
// Send player ID to the client
serverMsg := &pb.ServerMessage{
PlayerId: int32(playerID),
CurrentTick: 0,
}
if err := writeMessage(conn, serverMsg); err != nil {
log.Printf("Failed to send player ID to player %d: %v", playerID, err)
// Get client IP
remoteAddr := conn.RemoteAddr().String()
ip, _, err := net.SplitHostPort(remoteAddr)
if err != nil {
log.Printf("Failed to parse remote address: %v", err)
return
}
// Listen for incoming actions from this player
// Read initial message for player ID
reader := bufio.NewReader(conn)
// Wait for authentication
lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
log.Printf("Failed to read auth message length: %v", err)
return
}
messageLength := binary.BigEndian.Uint32(lengthBuf)
messageBuf := make([]byte, messageLength)
if _, err := io.ReadFull(reader, messageBuf); err != nil {
log.Printf("Failed to read auth message: %v", err)
return
}
batch := &pb.ActionBatch{}
if err := proto.Unmarshal(messageBuf, batch); err != nil {
log.Printf("Failed to unmarshal auth message: %v", err)
return
}
if len(batch.Actions) == 0 {
log.Printf("No auth action received")
return
}
action := batch.Actions[0]
var playerID int
var authErr error
if batch.ProtocolVersion == 0 {
response := &pb.ServerMessage{
AuthSuccess: false,
ErrorMessage: "Client using outdated protocol (pre-versioning)",
ProtocolVersion: protoVersion,
}
writeMessage(conn, response)
return
}
if batch.ProtocolVersion < protoVersion {
response := &pb.ServerMessage{
AuthSuccess: false,
ErrorMessage: fmt.Sprintf("Client protocol version too old (client: %d, required: %d)", batch.ProtocolVersion, protoVersion),
ProtocolVersion: protoVersion,
}
writeMessage(conn, response)
return
}
switch action.Type {
case pb.Action_REGISTER:
if err := db.CheckRegistrationLimit(ip); err != nil {
response := &pb.ServerMessage{
AuthSuccess: false,
ErrorMessage: err.Error(),
}
writeMessage(conn, response)
return
}
playerID, authErr = db.RegisterPlayer(action.Username, action.Password)
case pb.Action_LOGIN:
playerID, authErr = db.AuthenticatePlayer(action.Username, action.Password)
default:
log.Printf("Invalid initial action type: %v", action.Type)
return
}
// Send auth response
response := &pb.ServerMessage{
PlayerId: int32(playerID),
AuthSuccess: authErr == nil,
}
if authErr != nil {
response.ErrorMessage = authErr.Error()
if err := writeMessage(conn, response); err != nil {
log.Printf("Failed to send auth response: %v", err)
}
return
}
// Load last known position
x, y, err := db.LoadPlayerState(playerID)
if err != nil {
log.Printf("Error loading state for player %d: %v", playerID, err)
x, y = 5, 5 // Default position
}
username, err := db.GetUsername(playerID)
if err != nil {
log.Printf("Error getting username for player %d: %v", playerID, err)
return
}
player := &Player{
ID: playerID,
X: x,
Y: y,
Username: username,
}
// 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
playerConns[playerID] = conn
mu.Unlock()
// Send initial state with correct position
response = &pb.ServerMessage{
PlayerId: int32(playerID),
AuthSuccess: true,
Players: []*pb.PlayerState{{
PlayerId: int32(playerID),
X: int32(x),
Y: int32(y),
Username: username,
}},
ProtocolVersion: protoVersion,
}
// 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)
}
mu.Lock()
delete(players, playerID)
delete(playerConns, playerID)
delete(actionQueue, playerID)
mu.Unlock()
log.Printf("Player %d disconnected", playerID)
}()
// Send player ID to client
if err := writeMessage(conn, response); err != nil {
log.Printf("Failed to send player ID: %v", err)
return
}
fmt.Printf("Player %d connected\n", playerID)
// Listen for incoming actions from this player
for {
// Read message length
lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
log.Printf("Error reading message length from player %d: %v", playerID, err)
delete(players, playerID)
delete(playerConns, playerID)
delete(actionQueue, playerID)
if err == io.EOF {
log.Printf("Player %d disconnected gracefully", playerID)
} else {
log.Printf("Error reading message length from player %d: %v", playerID, err)
}
return
}
messageLength := binary.BigEndian.Uint32(lengthBuf)
@ -104,9 +264,6 @@ func handleConnection(conn net.Conn) {
messageBuf := make([]byte, messageLength)
if _, err := io.ReadFull(reader, messageBuf); err != nil {
log.Printf("Error reading message from player %d: %v", playerID, err)
delete(players, playerID)
delete(playerConns, playerID)
delete(actionQueue, playerID)
return
}
@ -120,10 +277,7 @@ func handleConnection(conn net.Conn) {
if batch.PlayerId == int32(playerID) {
for _, action := range batch.Actions {
if action.Type == pb.Action_DISCONNECT {
log.Printf("Player %d disconnected gracefully", playerID)
delete(players, playerID)
delete(playerConns, playerID)
delete(actionQueue, playerID)
log.Printf("Player %d requested disconnect", playerID)
return
}
}
@ -133,11 +287,17 @@ func handleConnection(conn net.Conn) {
}
func addChatMessage(playerID int32, content string) {
player, exists := players[int(playerID)]
if !exists {
return
}
chatMutex.Lock()
defer chatMutex.Unlock()
msg := &pb.ChatMessage{
PlayerId: playerID,
Username: player.Username,
Content: content,
Timestamp: time.Now().UnixNano(),
}
@ -185,6 +345,7 @@ func processActions() {
PlayerId: int32(id),
X: int32(p.X),
Y: int32(p.Y),
Username: p.Username,
})
p.Unlock()
}