From bf471b49340b5c6fa61ad311560e34ae0975c5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Sun, 9 Jun 2024 11:02:18 +0200 Subject: [PATCH] Update --- .idea/inspectionProfiles/Project_Default.xml | 7 + README.md | 3 +- client.go | 63 +++++-- commands.go | 172 +++++++++++-------- gun.go | 22 ++- item.go | 143 +++++++++++++-- main.go | 10 +- match.go | 128 ++++++-------- terminal_handler.go | 95 ++++++---- util.go | 13 -- 10 files changed, 420 insertions(+), 236 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..ac46435 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index fcd5271..00b6bf4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# telnetroulette +# Telnet roulette +# Just like Buckshot Roulette, but in telnet diff --git a/client.go b/client.go index 3954818..003f15d 100644 --- a/client.go +++ b/client.go @@ -2,10 +2,12 @@ package main import ( "github.com/BRNSystems/go-telnet" + "math/rand" "strconv" + "strings" ) -type Client struct { +type client struct { Client *telnet.Client Username string ID int @@ -14,15 +16,15 @@ type Client struct { context *telnet.Context writer *telnet.Writer reader *telnet.Reader - match *Match + match *match isHost bool HP int - items []Item + items []item // Add more fields as needed } -func NewClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Reader) int { - client := Client{} +func newClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Reader) int { + client := client{} clientsMutex.Lock() clients = append(clients, &client) @@ -39,19 +41,22 @@ func NewClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Re return clientID } -func RemoveClient(clientID int) { +func removeClient(clientID int) { clientsMutex.Lock() defer clientsMutex.Unlock() clients = append(clients[:clientID], clients[clientID+1:]...) } -func (client Client) send(message string) { +func (client *client) send(message string) { writer := *(client.writer) - writer.Write([]byte(message)) + _, err := writer.Write([]byte(message)) + if err != nil { + return + } } -func (client Client) sendMessage(message string, sender *Client) { - client.send("\a\r\n<" + sender.Username + "(" + strconv.Itoa(sender.ID) + ")> " + message + "\r\n") +func (client *client) sendMessage(message string, sender *client) { + client.send("\a\r\n<" + sender.getMentionName() + "> " + message + "\r\n") } func getIDByName(name string) int { @@ -77,3 +82,41 @@ func getByIDOrName(by string) int { } return -1 } + +func (client *client) renderHP() string { + var graphic strings.Builder + graphic.WriteString(strconv.Itoa(client.HP) + " / " + strconv.Itoa(client.match.maxHP) + " ") + for i := 1; i <= client.match.maxHP; i++ { + if client.HP >= i { + graphic.WriteString("█") + } else { + graphic.WriteString("░") + } + } + return graphic.String() +} + +func (client *client) renderItems() string { + if len(client.items) > 0 { + var out strings.Builder + for i := 0; i < len(client.items); i++ { + name := client.items[i].name() + out.WriteString(strconv.Itoa(i) + " - " + name + "\r\n") + } + return "My items are:\r\n" + out.String() + } else { + return "" + } +} + +func (client *client) getMentionName() string { + return client.Username + "(" + strconv.Itoa(client.ID) + ")" +} + +func (client *client) giveItem() { + client.items = append(client.items, item{ + kind: rand.Intn(client.match.itemCount), + match: client.match, + owner: client, + }) +} diff --git a/commands.go b/commands.go index 7afc8cd..9b53855 100644 --- a/commands.go +++ b/commands.go @@ -7,34 +7,34 @@ import ( "sync" ) -type Command struct { - commandHandlerFunc CommandHandlerFunc +type command struct { + commandHandlerFunc commandHandlerFunc description string } -type CommandHandlerFunc func(args []string, clientID int) string +type commandHandlerFunc func(args []string, clientID int) string -type CommandRegistry struct { - commands map[string]Command +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 newCommandRegistry() *commandRegistry { + return &commandRegistry{ + commands: make(map[string]command), } } -func (cr *CommandRegistry) RegisterCommand(command string, handler CommandHandlerFunc, desc string) { +func (cr *commandRegistry) registerCommand(commandName string, handler commandHandlerFunc, desc string) { cr.mutex.Lock() defer cr.mutex.Unlock() - cr.commands[command] = Command{ + cr.commands[commandName] = command{ commandHandlerFunc: handler, description: desc, } } -func (cr *CommandRegistry) GetCommandHandler(command string, clientID int) (CommandHandlerFunc, bool, int) { +func (cr *commandRegistry) getCommandHandler(command string, clientID int) (commandHandlerFunc, bool, int) { cr.mutex.Lock() defer cr.mutex.Unlock() commandEntry, ok := cr.commands[command] @@ -42,20 +42,33 @@ func (cr *CommandRegistry) GetCommandHandler(command string, clientID int) (Comm 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])`) +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 UseItemCommand(args []string, clientID int) string { +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 { @@ -73,59 +86,65 @@ func UseItemCommand(args []string, clientID int) string { } } 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) - clients[clientID].match.SendMessage("\r\nUsing item "+clients[clientID].items[itemID].name(), clients[clientID]) return "Used" } else { return "ERROR using item" } } -func ShootCommand(args []string, clientID int) string { - if clients[clientID].match != nil && clients[clientID].match.round > 0 && clients[clientID].match.Guestturn != 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 +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.magazine = clients[clientID].match.gun.Shoot(target) - if fired == 1 { - clients[clientID].match.Guestturn = clients[clientID].isHost - clients[clientID].match.SendMessage("Shot myself with a live", clients[clientID]) + 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 { - clients[clientID].match.SendMessage("Shot myself with a blank", clients[clientID]) + 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 { - if clients[clientID].isHost { - target = clients[clientID].match.guest - } else { - target = clients[clientID].match.host - } - fired, clients[clientID].match.gun.magazine = clients[clientID].match.gun.Shoot(target) - if fired == 1 { - clients[clientID].match.Guestturn = clients[clientID].isHost - clients[clientID].match.SendMessage("Shot "+target.Username+"("+strconv.Itoa(target.ID)+")"+"with a live", clients[clientID]) - } else { - clients[clientID].match.Guestturn = clients[clientID].isHost - clients[clientID].match.SendMessage("Shot "+target.Username+"("+strconv.Itoa(target.ID)+")"+"with a blank", clients[clientID]) - } + return "It is not your turn" } - return "SHOT" + } else { + return "Your are not in a match" + } - return "Error" } -func AcceptCommand(args []string, clientID int) string { +func acceptCommand(_ []string, clientID int) string { clientsMutex.Lock() defer clientsMutex.Unlock() matchesMutex.Lock() @@ -133,28 +152,29 @@ func AcceptCommand(args []string, clientID int) string { 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.Guestturn = false - clients[clientID].match.Start() + clients[clientID].match.guestsTurn = false + clients[clientID].match.start() return "Accepted" } return "No match to accept" } -func StartCommand(args []string, clientID int) string { +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{ + match := match{ host: clients[clientID], guest: clients[targetID], round: 0, - gun: Gun{ + 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() @@ -169,7 +189,7 @@ func StartCommand(args []string, clientID int) string { return "Invalid argument" } -func RandomCommand(args []string, clientID int) string { +func randomCommand(args []string, _ int) string { // Handle help command if len(args) == 2 { from, err1 := strconv.Atoi(args[0]) @@ -185,12 +205,12 @@ func RandomCommand(args []string, clientID int) string { return "No range provided" } -func ClearCommand(args []string, clientID int) string { +func clearCommand(_ []string, _ int) string { // Handle help command return "\033[H\033[2J\033[K" } -func SetNameCommand(args []string, clientID int) string { +func setNameCommand(args []string, clientID int) string { // Handle help command if len(args) >= 1 { newName := args[0] @@ -200,28 +220,28 @@ func SetNameCommand(args []string, clientID int) string { return "No name provided" } -func ListClientsCommand(args []string, clientID int) string { - message := "" +func listClientsCommand(_ []string, _ int) string { + var message strings.Builder for i, client := range clients { - message += strconv.Itoa(i) + ": " + client.Username + "\n" + message.WriteString(strconv.Itoa(i) + ": " + client.Username + "\n") } - return message + return message.String() } -func TellClientCommand(args []string, clientID int) 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 - newMessage := "" + 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 += arg + " " + newMessage.WriteString(arg + " ") } } - clients[toClientID].sendMessage(newMessage, clients[clientID]) + clients[toClientID].sendMessage(newMessage.String(), clients[clientID]) return "Message sent to " + toClientName } else { message = "Invalid recipient" diff --git a/gun.go b/gun.go index 7267a56..3d47bec 100644 --- a/gun.go +++ b/gun.go @@ -2,16 +2,15 @@ package main import "math/rand" -type Gun struct { +type gun struct { doubled bool magazine []bool } -// fire -func (g Gun) Shoot(target *Client) (int, []bool) { +func (g *gun) shoot(target *client) int { shot := 2 if len(g.magazine) == 0 { - return shot, g.magazine + return shot } shot = 0 if g.magazine[len(g.magazine)-1] { @@ -21,25 +20,24 @@ func (g Gun) Shoot(target *Client) (int, []bool) { } else { target.HP -= 1 } - target.send("\a") - target.match.Send("") + target.send("\a\a") + target.match.send("") } - g.magazine = g.Rack() + g.rack() g.doubled = false - return shot, g.magazine + return shot } -func (g Gun) Rack() []bool { +func (g *gun) rack() { g.magazine = g.magazine[:len(g.magazine)-1] - return g.magazine } -func (g Gun) Reload(count int) []bool { +func (g *gun) reload(count int) []bool { g.magazine = make([]bool, count) for i := 0; i < count; i++ { - g.magazine[i] = rand.Intn(6) <= 2 + g.magazine[i] = rand.Int63()&(1<<62) == 0 } return g.magazine } diff --git a/item.go b/item.go index fa06c49..713649d 100644 --- a/item.go +++ b/item.go @@ -1,33 +1,144 @@ package main -type Item struct { +import ( + "math" + "math/rand" + "strconv" +) + +type item struct { kind int - match *Match - owner *Client + match *match + owner *client } -func (i Item) name() string { +func (i *item) name() string { switch i.kind { case 0: - return "Doubler" + return "Saw" case 1: return "Beer" + case 2: + return "Phone" + case 3: + return "Magnifying glass" + case 4: + return "Expired medicine" + case 5: + return "Cigarette pack" + case 6: + return "Inverter" + case 7: + return "Adrenaline" default: return "None" } } -func (i Item) useItem(param1 int) { - switch i.kind { - case 0: - i.match.gun.doubled = true - i.match.SendMessage("I doubled the damage for 1 round", i.owner) - break - case 1: - i.match.gun.Rack() - i.match.SendMessage("I rack the magazine", i.owner) - i.match.announceRounds() - break +func (i *item) useItem(param1 int) { + successfully := false + if i.owner.isHost != i.owner.match.guestsTurn { + successfully = true + switch i.kind { + case 0: + i.match.gun.doubled = true + i.match.sendMessage("I doubled the damage for 1 round by sawing the shotgun", i.owner) + break + + case 1: + i.match.gun.rack() + i.match.sendMessage("I rack the magazine", i.owner) + i.match.announceRounds() + break + + case 2: + roundIndex := rand.Intn(len(i.match.gun.magazine) - 2) + round := i.match.gun.magazine[roundIndex] + roundString := "" + if round { + roundString = "live" + } else { + roundString = "blank" + } + roundForPlayer := int(math.Abs(float64(len(i.match.gun.magazine) - 2 - roundIndex))) + i.match.sendMessage("I used a phone", i.owner) + i.owner.send("The " + strconv.Itoa(roundForPlayer) + " round from now is " + roundString) + break + + case 3: + round := i.match.gun.magazine[len(i.match.gun.magazine)-1] + roundString := "" + if round { + roundString = "live" + } else { + roundString = "blank" + } + i.match.sendMessage("I used a magnifying glass", i.owner) + i.owner.send("Next round is " + roundString) + break + + case 4: + if rand.Int63()&(1<<62) == 0 { + for cnt := 0; cnt < 2; cnt++ { + if i.owner.HP < i.match.maxHP { + i.owner.HP++ + } + } + } else { + i.owner.HP-- + i.match.announceHP() + i.match.checkStatus() + } + i.match.sendMessage("I used an expired medicine", i.owner) + i.match.announceHP() + break + + case 5: + if i.owner.HP < i.match.maxHP { + i.owner.HP++ + } + i.match.sendMessage("I used a pack of cigarettes", i.owner) + i.match.announceHP() + break + + case 6: + i.match.gun.magazine[len(i.match.gun.magazine)-1] = !i.match.gun.magazine[len(i.match.gun.magazine)-1] + i.match.sendMessage("I used an inverter", i.owner) + break + case 7: + var otherPlayer *client + if i.owner.isHost { + otherPlayer = i.match.guest + } else { + otherPlayer = i.match.host + } + if param1 < len(otherPlayer.items) { + i.owner.items = append(i.owner.items, otherPlayer.items[param1]) + otherPlayer.items = append(otherPlayer.items[:param1], otherPlayer.items[param1:]...) + itemName := otherPlayer.items[param1].name() + if itemName[0] == 'a' || itemName[0] == 'e' || itemName[0] == 'i' || itemName[0] == 'o' || itemName[0] == 'u' || itemName[0] == 'y' { + itemName = " an " + itemName + } else { + itemName = " a " + itemName + } + i.match.sendMessage("I stole from "+otherPlayer.Username+"("+strconv.Itoa(otherPlayer.ID)+")"+itemName, i.owner) + } else { + i.owner.send("This requires another parameter") + successfully = false + } + break + } + } else { + i.owner.send("It is not your turn") + } + if successfully { + for cnt, item := range i.owner.items { + if item.kind == i.kind { + i.owner.items = append(i.owner.items[:cnt], i.owner.items[cnt+1:]...) + break + } + } + i.match.announceItems() } } diff --git a/main.go b/main.go index bd92b7d..b33805e 100644 --- a/main.go +++ b/main.go @@ -5,15 +5,15 @@ import ( "sync" ) -var clients []*Client +var clients []*client var clientsMutex sync.Mutex -var matches []*Match +var matches []*match var matchesMutex sync.Mutex func main() { addr := ":6969" - commandRegistry := NewCommandRegistry() - termHandler := NewInternalTerminalHandler("# ", + commandRegistry := newCommandRegistry() + termHandler := newInternalTerminalHandler("# ", ` ____ _ _ ____ _ _ _ / ___|| |__ ___ | |_ __ _ _ _ _ __ | _ \ ___ _ _| | ___| |_| |_ ___ \___ \| '_ \ / _ \| __/ _`+"`"+` | | | | '_ \ | |_) / _ \| | | | |/ _ \ __| __/ _ \ @@ -23,7 +23,7 @@ func main() { commandRegistry) // Register commands - RegisterCommands(commandRegistry) + registerCommands(commandRegistry) if err := telnet.ListenAndServe(addr, termHandler); nil != err { panic(err) diff --git a/match.go b/match.go index 093e3da..97efbd5 100644 --- a/match.go +++ b/match.go @@ -1,62 +1,59 @@ package main import ( - "math/rand" "strconv" ) -type Match struct { - host *Client - guest *Client - round int - itemCount int - gun Gun - Guestturn bool +type match struct { + host *client + guest *client + round int + maxHP int + itemCount int + gun gun + guestsTurn bool } -func (m *Match) LoadGun() { +func (m *match) loadGun() { switch m.round { case 1: - m.gun.magazine = m.gun.Reload(4) + m.gun.magazine = m.gun.reload(4) break case 2: - m.gun.magazine = m.gun.Reload(8) + m.gun.magazine = m.gun.reload(8) break case 3: - m.gun.magazine = m.gun.Reload(16) + m.gun.magazine = m.gun.reload(16) break default: - m.gun.magazine = m.gun.Reload(1) + m.gun.magazine = m.gun.reload(1) break } } -func (m *Match) Start() { - m.NextRound() +func (m *match) start() { + m.nextRound() } -func (m *Match) resetHP() { +func (m *match) resetHP() { + HP := 1 switch m.round { case 1: - m.host.HP = 5 - m.guest.HP = 5 + HP = 5 break case 2: - m.host.HP = 2 - m.guest.HP = 2 + HP = 2 break case 3: - m.host.HP = 4 - m.guest.HP = 4 - break - default: - m.host.HP = 1 - m.guest.HP = 1 + HP = 4 break } + m.host.HP = HP + m.guest.HP = HP + m.maxHP = HP } -func (m *Match) announceRounds() { +func (m *match) announceRounds() { live := 0 blank := 0 for _, round := range m.gun.magazine { @@ -70,19 +67,19 @@ func (m *Match) announceRounds() { doubled := "Shotgun is normal" if m.gun.doubled { - doubled = "Shotgun is doubled" + doubled = "Shotgun is sawed" } - m.Send("\r\nRounds are:\r\nLive - " + strconv.Itoa(live) + "\r\nBlank - " + strconv.Itoa(blank) + "\r\n" + doubled) + m.send("\r\nRounds are:\r\nLive - " + strconv.Itoa(live) + "\r\nBlank - " + strconv.Itoa(blank) + "\r\n" + doubled) } -func (m *Match) announceHP() { - m.Send("\r\nHPs are:\r\n" + - m.host.Username + "(" + strconv.Itoa(m.host.ID) + ")" + " - " + strconv.Itoa(m.host.HP) + "\r\n" + - m.guest.Username + "(" + strconv.Itoa(m.guest.ID) + ")" + " - " + strconv.Itoa(m.guest.HP)) +func (m *match) announceHP() { + m.send("\r\nHPs are:\r\n" + + m.host.getMentionName() + " - " + m.host.renderHP() + "\r\n" + + m.guest.getMentionName() + " - " + m.guest.renderHP()) } -func (m *Match) giveItems() { +func (m *match) giveItems() { count := 0 switch m.round { case 2: @@ -94,42 +91,25 @@ func (m *Match) giveItems() { } for i := 0; i < count; i++ { - m.host.items = append(m.host.items, Item{ - kind: rand.Intn(m.itemCount), //currently only item 0 exists - match: m, - owner: m.host, - }) - m.guest.items = append(m.host.items, Item{ - kind: rand.Intn(m.itemCount), //currently only item 0 exists - match: m, - owner: m.guest, - }) + m.host.giveItem() + m.guest.giveItem() } } -func (m *Match) announceItems() { - out := "" - for i := 0; i < len(m.host.items); i++ { - name := m.host.items[i].name() - out += strconv.Itoa(i) + " - " + name + "\r\n" - } - m.SendMessage("My items:\r\n"+out, m.host) - - out = "" - for i := 0; i < len(m.guest.items); i++ { - name := m.guest.items[i].name() - out += strconv.Itoa(i) + " - " + name + "\r\n" - } - m.SendMessage("My items:\r\n"+out, m.guest) +func (m *match) announceItems() { + m.sendMessage(m.host.renderItems(), m.host) + m.sendMessage(m.guest.renderItems(), m.guest) } -func (m *Match) NextRound() bool { +func (m *match) nextRound() bool { + defer m.announceTurn() defer m.announceItems() defer m.announceRounds() defer m.announceHP() - defer m.LoadGun() + defer m.loadGun() defer m.resetHP() defer m.giveItems() + m.guestsTurn = false if m.round < 3 { m.round++ } else if m.round == 3 { @@ -138,42 +118,42 @@ func (m *Match) NextRound() bool { return false } -func (m *Match) Send(message string) { +func (m *match) send(message string) { m.host.sendMessage(message, m.host) m.guest.sendMessage(message, m.guest) } -func (m *Match) SendMessage(message string, sender *Client) { +func (m *match) sendMessage(message string, sender *client) { m.host.sendMessage(message, sender) m.guest.sendMessage(message, sender) } -func (m *Match) announceTurn() { - if m.Guestturn { - m.guest.sendMessage("It is your turn to shoot", m.host) +func (m *match) announceTurn() { + if m.guestsTurn { + m.guest.sendMessage("It is your turn\a", m.host) } else { - m.host.sendMessage("It is your turn to shoot", m.guest) + m.host.sendMessage("It is your turn\a", m.guest) } } -func (m *Match) CheckStatus() { +func (m *match) checkStatus() { if m.host.HP < 1 { - m.SendMessage("I died", m.host) - m.SendMessage("I won", m.guest) + m.sendMessage("I died", m.host) + m.sendMessage("I won", m.guest) } if m.guest.HP < 1 { - m.SendMessage("I died", m.guest) - m.SendMessage("I won", m.host) + m.sendMessage("I died", m.guest) + m.sendMessage("I won", m.host) } if m.host.HP > 0 && m.guest.HP > 0 { if m.round >= 3 { - m.SendMessage("I won", m.host) - m.SendMessage("I won", m.guest) + m.sendMessage("I won", m.host) + m.sendMessage("I won", m.guest) } else { m.announceTurn() } } if len(m.gun.magazine) == 0 { - m.NextRound() + m.nextRound() } } diff --git a/terminal_handler.go b/terminal_handler.go index cbd7e55..5f0c335 100644 --- a/terminal_handler.go +++ b/terminal_handler.go @@ -6,29 +6,48 @@ import ( "strings" ) -type InternalTerminalHandler struct { +type internalTerminalHandler struct { ShellCharacter string WelcomeMessage string - CommandRegistry *CommandRegistry + CommandRegistry *commandRegistry } -func NewInternalTerminalHandler(shellCharacter, welcomeMessage string, commandRegistry *CommandRegistry) *InternalTerminalHandler { - return &InternalTerminalHandler{ +func newInternalTerminalHandler(shellCharacter, welcomeMessage string, commandRegistry *commandRegistry) *internalTerminalHandler { + return &internalTerminalHandler{ ShellCharacter: shellCharacter, WelcomeMessage: welcomeMessage, CommandRegistry: commandRegistry, } } -func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet.Writer, r telnet.Reader) { - clientID := NewClient(&ctx, &w, &r) +func clearLine(w telnet.Writer) { + _, err := w.RawWrite([]byte{0x1B, 0x5B, 0x4B}) + if err != nil { + return + } +} - defer RemoveClient(clientID) - w.RawWrite([]byte{0xff, 0xfb, 0x03}) //send char by char - w.RawWrite([]byte{0xff, 0xfb, 0x01}) //echo from server - w.Write([]byte(strings.ReplaceAll(handler.WelcomeMessage, "\n", "\r\n") + "\r\n")) +func (handler *internalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet.Writer, r telnet.Reader) { + clientID := newClient(&ctx, &w, &r) + + defer removeClient(clientID) + _, err := w.RawWrite([]byte{0xff, 0xfb, 0x03}) + if err != nil { + return + } //send char by char + _, err = w.RawWrite([]byte{0xff, 0xfb, 0x01}) + if err != nil { + return + } //echo from server + _, err = w.Write([]byte(strings.ReplaceAll(handler.WelcomeMessage, "\n", "\r\n") + "\r\n")) + if err != nil { + return + } //replace every \n with \r\n in WelcomeMessage - w.Write([]byte(handler.ShellCharacter)) + _, err = w.Write([]byte(handler.ShellCharacter)) + if err != nil { + return + } var lineBuffer []byte @@ -56,8 +75,11 @@ func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet clients[clientID].HistoryIndex++ } lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex]) - w.RawWrite([]byte{0x1B, 0x5B, 0x4B}) //clear line - w.Write(lineBuffer) + clearLine(w) + _, err := w.Write(lineBuffer) + if err != nil { + return + } } } else if CSISequence == 2 && b == 'B' { if len(lineBuffer) == 0 && len(clients[clientID].History) > 0 { @@ -65,23 +87,35 @@ func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet clients[clientID].HistoryIndex-- } lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex]) - w.RawWrite([]byte{0x1B, 0x5B, 0x4B}) //clear line - w.Write(lineBuffer) + clearLine(w) + _, err := w.Write(lineBuffer) + if err != nil { + return + } } } else if b == 0x7F { if len(lineBuffer) > 0 { - w.Write([]byte{0x08, 0x20, 0x08}) + _, err := w.Write([]byte{0x08, 0x20, 0x08}) + if err != nil { + return + } lineBuffer = lineBuffer[:len(lineBuffer)-1] } } else if b != '\r' && b != '\n' && b != 0 { CSISequence = 0 lineBuffer = append(lineBuffer, b) - w.RawWrite([]byte{b}) + _, err := w.RawWrite([]byte{b}) + if err != nil { + return + } } else if b == '\r' { - w.Write([]byte("\r\n")) + _, err := w.Write([]byte("\r\n")) + if err != nil { + return + } command := string(lineBuffer) args := strings.Fields(command) - var message string + var message strings.Builder if len(args) > 0 { cmd := args[0] @@ -91,32 +125,35 @@ func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet return } else if cmd == "help" { internalCommand = true - commands := "" + var commands strings.Builder for command, commandEntry := range handler.CommandRegistry.commands { - commands += command + " - " + commandEntry.description + "\r\n" + commands.WriteString(command + " - " + commandEntry.description + "\r\n") } // Handle help command if len(clients) > 0 { - clients[clientID].send("Just help yourself, " + clients[clientID].Username + "\r\nCommands:\r\n" + commands) + clients[clientID].send("Just help yourself, " + clients[clientID].Username + "\r\nCommands:\r\n" + commands.String()) } else { - clients[clientID].send("No clients available" + "\r\nCommands:\r\n" + commands) + clients[clientID].send("No clients available" + "\r\nCommands:\r\n" + commands.String()) } } - handlerFunc, ok, clientID := handler.CommandRegistry.GetCommandHandler(cmd, clientID) + handlerFunc, ok, clientID := handler.CommandRegistry.getCommandHandler(cmd, clientID) clients[clientID].HistoryIndex = 0 if ok || internalCommand { if ok { - message = handlerFunc(args[1:], clientID) + message.WriteString(handlerFunc(args[1:], clientID)) } clients[clientID].History = append(clients[clientID].History, command) } else { - message = "Command not found" + message.WriteString("Command not found") } - message += "\n" + handler.ShellCharacter + message.WriteString("\n" + handler.ShellCharacter) } else { - message = handler.ShellCharacter + message.WriteString(handler.ShellCharacter) + } + _, err = oi.LongWrite(w, []byte(strings.ReplaceAll(message.String(), "\n", "\r\n"))) + if err != nil { + return } - oi.LongWrite(w, []byte(strings.ReplaceAll(message, "\n", "\r\n"))) lineBuffer = nil } } diff --git a/util.go b/util.go index 50eb8fd..06ab7d0 100644 --- a/util.go +++ b/util.go @@ -1,14 +1 @@ package main - -import ( - "unicode" -) - -func isInt(s string) bool { - for _, c := range s { - if !unicode.IsDigit(c) { - return false - } - } - return true -}