diff --git a/go.mod b/go.mod index eb9fd35..52e98f7 100644 --- a/go.mod +++ b/go.mod @@ -9,5 +9,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect + golang.org/x/crypto v0.39.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2447b7b..06c7739 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= diff --git a/internal/game/data/account.go b/internal/game/data/account.go index 744d80b..a5971ce 100644 --- a/internal/game/data/account.go +++ b/internal/game/data/account.go @@ -11,7 +11,7 @@ func (ac AccountComponent) Type() ecs.ComponentType { } type PasswordComponent struct { - EncryptedPassword string + EncryptedPassword []byte } func (pc PasswordComponent) Type() ecs.ComponentType { diff --git a/internal/game/data/command.go b/internal/game/data/command.go index a30ecb9..646b644 100644 --- a/internal/game/data/command.go +++ b/internal/game/data/command.go @@ -65,8 +65,8 @@ type ArgName = string const ( ArgMessageContent ArgName = "messageContent" - ArgAccountName = "accountName" - ArgAccountPassword = "accountPassword" + ArgAccountName ArgName = "accountName" + ArgAccountPassword ArgName = "accountPassword" ) type Arg struct { @@ -88,8 +88,6 @@ type Command string const ( CommandSay Command = "say" CommandQuit = "quit" - CommandHelp = "help" - CommandSetName = "setname" CommandLogin = "login" CommandRegister = "register" ) diff --git a/internal/game/logic/command/commands.go b/internal/game/logic/command/commands.go index 24528e7..9296362 100644 --- a/internal/game/logic/command/commands.go +++ b/internal/game/logic/command/commands.go @@ -2,11 +2,14 @@ package command import ( "code.haedhutner.dev/mvv/LastMUD/internal/game/logic/world" + "code.haedhutner.dev/mvv/LastMUD/internal/logging" "regexp" "time" "code.haedhutner.dev/mvv/LastMUD/internal/ecs" "code.haedhutner.dev/mvv/LastMUD/internal/game/data" + + "golang.org/x/crypto/bcrypt" ) func HandleSay(w *ecs.World, _ time.Duration, player ecs.Entity, args data.ArgsMap) (err error) { @@ -51,7 +54,8 @@ func HandleQuit(w *ecs.World, _ time.Duration, player ecs.Entity, _ data.ArgsMap return } -var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]{1,24}$`) +var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]{1,16}$`) +var passwordRegex = regexp.MustCompile(`^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{}|;:',.<>/?]{6,12}$`) func HandleRegister(w *ecs.World, delta time.Duration, player ecs.Entity, args data.ArgsMap) (err error) { accountName, err := arg[string](args, data.ArgAccountName) @@ -61,16 +65,42 @@ func HandleRegister(w *ecs.World, delta time.Duration, player ecs.Entity, args d } if !usernameRegex.MatchString(accountName) { - world.SendMessageToPlayer(w, player, "Registration: Username must only contain letters, numbers, dashes (-) and underscores (_), and be at most 24 characters in length.") + world.SendMessageToPlayer(w, player, "Registration: Username must only contain letters, numbers, dashes (-) and underscores (_), and be at most 16 characters in length.") + return } - //accountPassword, err := arg[string](args, data.ArgAccountPassword) - // - //if err != nil { - // return err - //} + accountPassword, err := arg[string](args, data.ArgAccountPassword) - // TODO: validate username and password, encrypt password, etc. + if err != nil { + return err + } + + if !passwordRegex.MatchString(accountPassword) { + world.SendMessageToPlayer(w, player, "Registration: Password must be between 6 and 12 characters in length") + return + } + + // TODO: Validate username doesn't exist already + + encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(accountPassword), bcrypt.DefaultCost) + + account := world.CreateAccount(w, accountName, encryptedPassword) + + ecs.SetComponent(w, player, data.AccountComponent{Account: account}) + + world.SendMessageToPlayer(w, player, "Account created successfully! Welcome to LastMUD!") + + defaultRoom, err := ecs.GetResource[ecs.Entity](w, data.ResourceDefaultRoom) + + if err != nil { + logging.Error("Unable to locate default room") + world.SendMessageToPlayer(w, player, "Welcome to LastMUD! Your account was created, but you could not be joined to a room. Please try again later!") + return + } + + ecs.SetComponent(w, player, data.NameComponent{Name: accountName}) + ecs.SetComponent(w, player, data.InRoomComponent{Room: defaultRoom}) + ecs.SetComponent(w, player, data.PlayerStateComponent{State: data.PlayerStatePlaying}) return } diff --git a/internal/game/logic/systems.go b/internal/game/logic/systems.go index 9d93e63..a334549 100644 --- a/internal/game/logic/systems.go +++ b/internal/game/logic/systems.go @@ -23,6 +23,6 @@ func CreateSystems() []*ecs.System { // Command Handlers ecs.CreateSystem("SayCommandHandler", CommandOffset+0, command.CreateHandler(data.CommandSay, command.HandleSay)), ecs.CreateSystem("QuitCommandHandler", CommandOffset+10, command.CreateHandler(data.CommandQuit, command.HandleQuit)), - // ecs.CreateSystem("RegisterCommandHandler", CommandOffset+20, command.CreateHandler(data.CommandRegister, command.HandleRegister)), + ecs.CreateSystem("RegisterCommandHandler", CommandOffset+60, command.CreateHandler(data.CommandRegister, command.HandleRegister)), } } diff --git a/internal/game/logic/world/account.go b/internal/game/logic/world/account.go index a6d5b9b..f47f9a8 100644 --- a/internal/game/logic/world/account.go +++ b/internal/game/logic/world/account.go @@ -5,7 +5,7 @@ import ( "code.haedhutner.dev/mvv/LastMUD/internal/game/data" ) -func CreateAccount(world *ecs.World, username, encryptedPassword string) ecs.Entity { +func CreateAccount(world *ecs.World, username string, encryptedPassword []byte) ecs.Entity { account := ecs.NewEntity() ecs.SetComponent(world, account, data.NameComponent{Name: username}) diff --git a/internal/game/logic/world/player.go b/internal/game/logic/world/player.go index b095fd2..9967fdc 100644 --- a/internal/game/logic/world/player.go +++ b/internal/game/logic/world/player.go @@ -27,3 +27,13 @@ func SendMessageToPlayer(world *ecs.World, player ecs.Entity, message string) { CreateGameOutput(world, connId.ConnectionId, message) } + +func SendDisconnectMessageToPlayer(world *ecs.World, player ecs.Entity, message string) { + connId, ok := ecs.GetComponent[data.ConnectionIdComponent](world, player) + + if !ok { + return + } + + CreateClosingGameOutput(world, connId.ConnectionId, []byte(message)) +} diff --git a/internal/game/logic/world/room.go b/internal/game/logic/world/room.go index 4934285..b00cede 100644 --- a/internal/game/logic/world/room.go +++ b/internal/game/logic/world/room.go @@ -19,3 +19,7 @@ func CreateRoom( return entity } + +func MovePlayerToRoom(world *ecs.World, player, room ecs.Entity) { + ecs.SetComponent(world, player, data.InRoomComponent{Room: room}) +} diff --git a/internal/server/server.go b/internal/server/server.go index 2b79ec4..d5c8d95 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -119,6 +119,7 @@ func (srv *Server) consumeGameOutput() { output := srv.lastmudgame.ConsumeNextOutput() if output == nil { + time.Sleep(20 * time.Millisecond) continue }