Compare commits
	
		
			3 Commits
		
	
	
		
			3f7205d73e
			...
			d25ee09155
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d25ee09155 | |||
| e3c570349c | |||
| 27da845b11 | 
							
								
								
									
										60
									
								
								db/db.go
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								db/db.go
									
									
									
									
									
								
							| @ -5,6 +5,8 @@ import ( | |||||||
| 	"database/sql" | 	"database/sql" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	_ "github.com/mattn/go-sqlite3" | 	_ "github.com/mattn/go-sqlite3" | ||||||
| @ -17,6 +19,64 @@ var ( | |||||||
| 	ErrInvalidCredentials = errors.New("invalid username or password") | 	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 { | func InitDB(dbPath string) error { | ||||||
| 	var err error | 	var err error | ||||||
| 	db, err = sql.Open("sqlite3", dbPath) | 	db, err = sql.Open("sqlite3", dbPath) | ||||||
|  | |||||||
							
								
								
									
										46
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								main.go
									
									
									
									
									
								
							| @ -36,7 +36,6 @@ var ( | |||||||
| 	mu          sync.RWMutex                 // Add mutex for protecting shared maps | 	mu          sync.RWMutex                 // Add mutex for protecting shared maps | ||||||
| 	chatHistory = make([]*pb.ChatMessage, 0, 100) | 	chatHistory = make([]*pb.ChatMessage, 0, 100) | ||||||
| 	chatMutex   sync.RWMutex | 	chatMutex   sync.RWMutex | ||||||
| 	nextPlayerID = 1 // Assuming player IDs start from 1 |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| @ -55,6 +54,15 @@ func main() { | |||||||
| 	ticker := time.NewTicker(tickRate) | 	ticker := time.NewTicker(tickRate) | ||||||
| 	defer ticker.Stop() | 	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 | 	// Handle incoming connections in a separate goroutine | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for { | 		for { | ||||||
| @ -76,6 +84,14 @@ func main() { | |||||||
| func handleConnection(conn net.Conn) { | func handleConnection(conn net.Conn) { | ||||||
| 	defer conn.Close() | 	defer conn.Close() | ||||||
|  |  | ||||||
|  | 	// 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 | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Read initial message for player ID | 	// Read initial message for player ID | ||||||
| 	reader := bufio.NewReader(conn) | 	reader := bufio.NewReader(conn) | ||||||
|  |  | ||||||
| @ -130,6 +146,14 @@ func handleConnection(conn net.Conn) { | |||||||
|  |  | ||||||
| 	switch action.Type { | 	switch action.Type { | ||||||
| 	case pb.Action_REGISTER: | 	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) | 		playerID, authErr = db.RegisterPlayer(action.Username, action.Password) | ||||||
| 	case pb.Action_LOGIN: | 	case pb.Action_LOGIN: | ||||||
| 		playerID, authErr = db.AuthenticatePlayer(action.Username, action.Password) | 		playerID, authErr = db.AuthenticatePlayer(action.Username, action.Password) | ||||||
| @ -201,11 +225,14 @@ 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 | 	// Ensure player state is saved on any kind of disconnect | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		if err := db.SavePlayerState(playerID, player.X, player.Y); err != nil { | 		if err := db.SavePlayerState(playerID, player.X, player.Y); err != nil { | ||||||
| 			log.Printf("Error saving state for player %d: %v", playerID, err) | 			log.Printf("Error saving state for player %d: %v", playerID, err) | ||||||
| 		} | 		} | ||||||
|  | 		addSystemMessage(fmt.Sprintf("%s disconnected", player.Username)) | ||||||
| 		mu.Lock() | 		mu.Lock() | ||||||
| 		delete(players, playerID) | 		delete(players, playerID) | ||||||
| 		delete(playerConns, playerID) | 		delete(playerConns, playerID) | ||||||
| @ -284,6 +311,23 @@ func addChatMessage(playerID int32, content string) { | |||||||
| 	chatHistory = append(chatHistory, msg) | 	chatHistory = append(chatHistory, msg) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func addSystemMessage(content string) { | ||||||
|  | 	chatMutex.Lock() | ||||||
|  | 	defer chatMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	msg := &pb.ChatMessage{ | ||||||
|  | 		PlayerId:  0, // System messages use ID 0 | ||||||
|  | 		Username:  "System", | ||||||
|  | 		Content:   content, | ||||||
|  | 		Timestamp: time.Now().UnixNano(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(chatHistory) >= 100 { | ||||||
|  | 		chatHistory = chatHistory[1:] | ||||||
|  | 	} | ||||||
|  | 	chatHistory = append(chatHistory, msg) | ||||||
|  | } | ||||||
|  |  | ||||||
| func processActions() { | func processActions() { | ||||||
| 	mu.Lock() | 	mu.Lock() | ||||||
| 	defer mu.Unlock() | 	defer mu.Unlock() | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user