diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3997bea --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.db \ No newline at end of file diff --git a/actions/actions.pb.go b/actions/actions.pb.go index 3e1ada6..bcc75ab 100644 --- a/actions/actions.pb.go +++ b/actions/actions.pb.go @@ -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 ( diff --git a/actions/actions.proto b/actions/actions.proto index 6b3c234..4e27527 100644 --- a/actions/actions.proto +++ b/actions/actions.proto @@ -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; } diff --git a/db/db.go b/db/db.go new file mode 100644 index 0000000..a2ba9c4 --- /dev/null +++ b/db/db.go @@ -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] = ®istrationAttempt{ + 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 +} diff --git a/go.mod b/go.mod index 3559eac..02c6a14 100644 --- a/go.mod +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum index be0e561..f75bbee 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/main.go b/main.go index 4b02336..3328bd9 100644 --- a/main.go +++ b/main.go @@ -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() }