1
0
game/server/cmd/login/main.go

234 lines
5.0 KiB
Go

package main
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"log"
"net"
"server/internal/db"
"time"
"golang.org/x/crypto/bcrypt"
)
type LoginServer struct {
listener net.Listener
database *db.Database
worldURL string
serverID string
}
type LoginRequest struct {
Type string `json:"type"`
Username string `json:"username"`
Password string `json:"password"`
}
type LoginResponse struct {
Type string `json:"type"`
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Token string `json:"token,omitempty"`
WorldURL string `json:"worldUrl,omitempty"`
PlayerID int64 `json:"playerId,omitempty"`
}
type RegisterRequest struct {
Type string `json:"type"`
Username string `json:"username"`
Password string `json:"password"`
}
type RegisterResponse struct {
Type string `json:"type"`
Success bool `json:"success"`
Message string `json:"message,omitempty"`
}
func main() {
var (
port = flag.String("port", "8081", "Login server port")
dbDSN = flag.String("db", "user:password@tcp(localhost:3306)/game", "Database DSN")
worldURL = flag.String("world", "localhost:8082", "World server URL")
)
flag.Parse()
database, err := db.New(*dbDSN)
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer database.Close()
server := &LoginServer{
database: database,
worldURL: *worldURL,
serverID: generateServerID(),
}
listener, err := net.Listen("tcp", ":"+*port)
if err != nil {
log.Fatalf("Failed to start login server: %v", err)
}
server.listener = listener
log.Printf("Login server started on port %s (ID: %s)", *port, server.serverID)
go server.cleanupSessions()
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("Accept error: %v", err)
continue
}
go server.handleConnection(conn)
}
}
func (s *LoginServer) handleConnection(conn net.Conn) {
defer conn.Close()
decoder := json.NewDecoder(conn)
encoder := json.NewEncoder(conn)
for {
var msg json.RawMessage
if err := decoder.Decode(&msg); err != nil {
return
}
var baseMsg struct {
Type string `json:"type"`
}
if err := json.Unmarshal(msg, &baseMsg); err != nil {
continue
}
switch baseMsg.Type {
case "login":
var req LoginRequest
if err := json.Unmarshal(msg, &req); err != nil {
continue
}
s.handleLogin(req, encoder)
case "register":
var req RegisterRequest
if err := json.Unmarshal(msg, &req); err != nil {
continue
}
s.handleRegister(req, encoder)
}
}
}
func (s *LoginServer) handleLogin(req LoginRequest, encoder *json.Encoder) {
player, err := s.database.GetPlayerByUsername(req.Username)
if err != nil {
encoder.Encode(LoginResponse{
Type: "loginResponse",
Success: false,
Message: "Invalid username or password",
})
return
}
if err := bcrypt.CompareHashAndPassword([]byte(player.PasswordHash), []byte(req.Password)); err != nil {
encoder.Encode(LoginResponse{
Type: "loginResponse",
Success: false,
Message: "Invalid username or password",
})
return
}
token := generateToken()
if err := s.database.CreateSession(player.ID, token, "world", s.serverID, 24*time.Hour); err != nil {
encoder.Encode(LoginResponse{
Type: "loginResponse",
Success: false,
Message: "Failed to create session",
})
return
}
s.database.UpdateLastLogin(player.ID)
encoder.Encode(LoginResponse{
Type: "loginResponse",
Success: true,
Token: token,
WorldURL: s.worldURL,
PlayerID: player.ID,
})
}
func (s *LoginServer) handleRegister(req RegisterRequest, encoder *json.Encoder) {
if len(req.Username) < 3 || len(req.Username) > 20 {
encoder.Encode(RegisterResponse{
Type: "registerResponse",
Success: false,
Message: "Username must be between 3 and 20 characters",
})
return
}
if len(req.Password) < 6 {
encoder.Encode(RegisterResponse{
Type: "registerResponse",
Success: false,
Message: "Password must be at least 6 characters",
})
return
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
encoder.Encode(RegisterResponse{
Type: "registerResponse",
Success: false,
Message: "Failed to process password",
})
return
}
_, err = s.database.CreatePlayer(req.Username, string(hashedPassword))
if err != nil {
encoder.Encode(RegisterResponse{
Type: "registerResponse",
Success: false,
Message: "Username already exists",
})
return
}
encoder.Encode(RegisterResponse{
Type: "registerResponse",
Success: true,
Message: "Registration successful",
})
}
func (s *LoginServer) cleanupSessions() {
ticker := time.NewTicker(1 * time.Hour)
defer ticker.Stop()
for range ticker.C {
if err := s.database.CleanExpiredSessions(); err != nil {
log.Printf("Failed to clean expired sessions: %v", err)
}
}
}
func generateToken() string {
b := make([]byte, 32)
rand.Read(b)
return hex.EncodeToString(b)
}
func generateServerID() string {
return fmt.Sprintf("login-%d", time.Now().Unix())
}