package main import ( "math/rand" "strconv" "strings" "sync" ) type command struct { commandHandlerFunc commandHandlerFunc description string } type commandHandlerFunc func(args []string, clientID int) string type commandRegistry struct { commands map[string]command mutex sync.Mutex // Mutex to safely access commands map } func newCommandRegistry() *commandRegistry { return &commandRegistry{ commands: make(map[string]command), } } func (cr *commandRegistry) registerCommand(commandName string, handler commandHandlerFunc, desc string) { cr.mutex.Lock() defer cr.mutex.Unlock() cr.commands[commandName] = command{ commandHandlerFunc: handler, description: desc, } } func (cr *commandRegistry) getCommandHandler(command string, clientID int) (commandHandlerFunc, bool, int) { cr.mutex.Lock() defer cr.mutex.Unlock() commandEntry, ok := cr.commands[command] handler := commandEntry.commandHandlerFunc return handler, ok, clientID } func registerCommands(registry *commandRegistry) { registry.registerCommand("clear", clearCommand, `Clears your screen (clear)`) registry.registerCommand("setname", setNameCommand, `Sets your name (setname yourname[string])`) registry.registerCommand("list", listClientsCommand, `Lists clients (list)`) registry.registerCommand("rng", randomCommand, `Generates a random integer (rng from[int] to[int{bigger than from}], both need to be above zero)`) registry.registerCommand("tell", tellClientCommand, `Tell a client (tell client[id or string{username}])`) registry.registerCommand("challenge", startCommand, `Challenge a client (challenge client[id or string{username}])`) registry.registerCommand("accept", acceptCommand, `Accepts a challenge (accept)`) registry.registerCommand("shoot", shootCommand, `Shoots at a client (shoot target[int or string], accepts self as a parameter) {Shooting self with a blank keeps your turn}`) registry.registerCommand("useitem", useItemCommand, `Uses an item in a match (useitem index[number])`) registry.registerCommand("status", statusCommand, `Prints the game status (status)`) // Add more commands here... } func statusCommand(_ []string, clientID int) string { if clients[clientID].match != nil && clients[clientID].match.round > 0 { clients[clientID].match.announceHP() clients[clientID].match.announceItems() clients[clientID].match.announceRounds() clients[clientID].match.announceTurn() return "" } else { return "Not in match" } } func useItemCommand(args []string, clientID int) string { param := -1 itemID := -1 if len(args) >= 2 { var err error param, err = strconv.Atoi(args[1]) if err != nil { param = -1 } } if len(args) >= 1 { var err error itemID, err = strconv.Atoi(args[0]) if err != nil { itemID = -1 } } if itemID >= 0 && itemID < clients[clientID].match.itemCount { clients[clientID].match.sendMessage("\r\nUsing item "+clients[clientID].items[itemID].name(), clients[clientID]) clients[clientID].items[itemID].useItem(param) return "Used" } else { return "ERROR using item" } } func shootCommand(args []string, clientID int) string { if clients[clientID].match != nil && clients[clientID].match.round > 0 { if clients[clientID].match.guestsTurn != clients[clientID].isHost { defer clients[clientID].match.checkStatus() defer clients[clientID].match.announceHP() defer clients[clientID].match.announceRounds() self := false if len(args) == 1 { if strings.ToLower(args[0]) == "self" { self = true } else if getByIDOrName(args[0]) == clientID { self = true } } var fired int var target *client if self { target = clients[clientID] fired = clients[clientID].match.gun.shoot(target) if fired == 1 { clients[clientID].match.guestsTurn = clients[clientID].isHost clients[clientID].match.sendMessage("Shot myself with a live", clients[clientID]) } else { clients[clientID].match.sendMessage("Shot myself with a blank", clients[clientID]) } } else { if clients[clientID].isHost { target = clients[clientID].match.guest } else { target = clients[clientID].match.host } fired = clients[clientID].match.gun.shoot(target) if fired == 1 { clients[clientID].match.guestsTurn = clients[clientID].isHost clients[clientID].match.sendMessage("Shot "+target.Username+"("+strconv.Itoa(target.ID)+")"+"with a live", clients[clientID]) } else { clients[clientID].match.guestsTurn = clients[clientID].isHost clients[clientID].match.sendMessage("Shot "+target.Username+"("+strconv.Itoa(target.ID)+")"+"with a blank", clients[clientID]) } } return "SHOT" } else { return "It is not your turn" } } else { return "Your are not in a match" } } func acceptCommand(_ []string, clientID int) string { clientsMutex.Lock() defer clientsMutex.Unlock() matchesMutex.Lock() defer matchesMutex.Unlock() if clients[clientID].match != nil && clients[clientID].match.guest == clients[clientID] { clients[clientID].match.host.sendMessage("Accepted", clients[clientID]) clients[clientID].isHost = false clients[clientID].match.guestsTurn = false clients[clientID].match.start() return "Accepted" } return "No match to accept" } func startCommand(args []string, clientID int) string { if len(args) == 1 { targetID := getByIDOrName(args[0]) if targetID >= 0 && targetID != clientID && (clients[clientID].match == nil || clients[clientID].match.round <= 0) { match := match{ host: clients[clientID], guest: clients[targetID], round: 0, gun: gun{ doubled: false, }, } clientsMutex.Lock() clients[clientID].match = &match clients[targetID].match = &match match.itemCount = 8 clients[clientID].isHost = true clients[targetID].sendMessage("Type \"accept\" to accept a match.", clients[clientID]) clientsMutex.Unlock() matchesMutex.Lock() matches = append(matches, &match) matchesMutex.Unlock() return "Successfully challenged" } else { return "Unknown user" } } return "Invalid argument" } func randomCommand(args []string, _ int) string { // Handle help command if len(args) == 2 { from, err1 := strconv.Atoi(args[0]) to, err2 := strconv.Atoi(args[1]) if err1 != nil || err2 != nil { return "Invalid argument" } if to-from <= 0 { return "Invalid range" } return "The number is " + strconv.Itoa(rand.Intn(to-from)+from) } return "No range provided" } func clearCommand(_ []string, _ int) string { // Handle help command return "\033[H\033[2J\033[K" } func setNameCommand(args []string, clientID int) string { // Handle help command if len(args) >= 1 { newName := args[0] clients[clientID].Username = newName return "Name set to " + newName } return "No name provided" } func listClientsCommand(_ []string, _ int) string { var message strings.Builder for i, client := range clients { message.WriteString(strconv.Itoa(i) + ": " + client.Username + "\n") } return message.String() } func tellClientCommand(args []string, clientID int) string { message := "Message not delivered" if len(args) >= 2 { toClientID := getByIDOrName(args[0]) if toClientID >= 0 { toClientName := clients[toClientID].Username var newMessage strings.Builder //all the remaining arguments are a part of the message, join them with space for i, arg := range args { if i > 0 { newMessage.WriteString(arg + " ") } } clients[toClientID].sendMessage(newMessage.String(), clients[clientID]) return "Message sent to " + toClientName } else { message = "Invalid recipient" } } return message }