This commit is contained in:
Bruno Rybársky 2024-06-08 23:23:20 +02:00
parent 111564b666
commit 4e195c6ba3
6 changed files with 311 additions and 60 deletions

@ -16,7 +16,8 @@ type Client struct {
reader *telnet.Reader reader *telnet.Reader
match *Match match *Match
isHost bool isHost bool
hp int HP int
items []Item
// Add more fields as needed // Add more fields as needed
} }
@ -34,7 +35,7 @@ func NewClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Re
clients[clientID].writer = writer clients[clientID].writer = writer
clients[clientID].reader = reader clients[clientID].reader = reader
clients[clientID].isHost = false clients[clientID].isHost = false
clients[clientID].hp = -1 clients[clientID].HP = -1
return clientID return clientID
} }

@ -7,47 +7,85 @@ import (
"sync" "sync"
) )
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 { type CommandRegistry struct {
commands map[string]CommandHandlerFunc commands map[string]Command
mutex sync.Mutex // Mutex to safely access commands map mutex sync.Mutex // Mutex to safely access commands map
} }
func NewCommandRegistry() *CommandRegistry { func NewCommandRegistry() *CommandRegistry {
return &CommandRegistry{ return &CommandRegistry{
commands: make(map[string]CommandHandlerFunc), commands: make(map[string]Command),
} }
} }
func (cr *CommandRegistry) RegisterCommand(command string, handler CommandHandlerFunc) { func (cr *CommandRegistry) RegisterCommand(command string, handler CommandHandlerFunc, desc string) {
cr.mutex.Lock() cr.mutex.Lock()
defer cr.mutex.Unlock() defer cr.mutex.Unlock()
cr.commands[command] = handler cr.commands[command] = 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() cr.mutex.Lock()
defer cr.mutex.Unlock() defer cr.mutex.Unlock()
handler, ok := cr.commands[command] commandEntry, ok := cr.commands[command]
handler := commandEntry.commandHandlerFunc
return handler, ok, clientID return handler, ok, clientID
} }
func RegisterCommands(registry *CommandRegistry) { func RegisterCommands(registry *CommandRegistry) {
registry.RegisterCommand("help", HelpCommand) registry.RegisterCommand("clear", ClearCommand, `Clears your screen (clear)`)
registry.RegisterCommand("setname", SetNameCommand) registry.RegisterCommand("setname", SetNameCommand, `Sets your name (setname yourname[string])`)
registry.RegisterCommand("list", ListClientsCommand) registry.RegisterCommand("list", ListClientsCommand, `Lists clients (list)`)
registry.RegisterCommand("tell", TellClientCommand) registry.RegisterCommand("rng", RandomCommand, `Generates a random integer (rng from[int] to[int{bigger than from}], both need to be above zero)`)
registry.RegisterCommand("clear", ClearCommand) registry.RegisterCommand("tell", TellClientCommand, `Tell a client (tell client[id or string{username}])`)
registry.RegisterCommand("rng", RandomCommand) registry.RegisterCommand("challenge", StartCommand, `Challenge a client (challenge client[id or string{username}])`)
registry.RegisterCommand("challenge", StartCommand) registry.RegisterCommand("accept", AcceptCommand, `Accepts a challenge (accept)`)
registry.RegisterCommand("accept", AcceptCommand) 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("shoot", ShootCommand) registry.RegisterCommand("useitem", UseItemCommand, `Uses an item in a match (useitem index[number])`)
// Add more commands here... // Add more commands here...
} }
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].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 { func ShootCommand(args []string, clientID int) string {
if clients[clientID].match.round >= 0 { 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 self := false
if len(args) == 1 { if len(args) == 1 {
if strings.ToLower(args[0]) == "self" { if strings.ToLower(args[0]) == "self" {
@ -56,26 +94,33 @@ func ShootCommand(args []string, clientID int) string {
self = true self = true
} }
} }
var fired bool var fired int
var target *Client
if self { if self {
fired = clients[clientID].match.gun.Shoot(clients[clientID]) target = clients[clientID]
if fired { fired, clients[clientID].match.gun.magazine = clients[clientID].match.gun.Shoot(target)
return "Shot self with a live" if fired == 1 {
clients[clientID].match.Guestturn = clients[clientID].isHost
clients[clientID].match.SendMessage("Shot myself with a live", clients[clientID])
} else { } else {
return "Shot self with a blank" clients[clientID].match.SendMessage("Shot myself with a blank", clients[clientID])
} }
} else { } else {
if clients[clientID].isHost { if clients[clientID].isHost {
fired = clients[clientID].match.gun.Shoot(clients[clientID].match.guest) target = clients[clientID].match.guest
} else { } else {
fired = clients[clientID].match.gun.Shoot(clients[clientID].match.host) target = clients[clientID].match.host
} }
if fired { fired, clients[clientID].match.gun.magazine = clients[clientID].match.gun.Shoot(target)
return "Shot " + args[0] + "with a live" 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 { } else {
return "Shot " + args[0] + "with a blank" clients[clientID].match.Guestturn = clients[clientID].isHost
clients[clientID].match.SendMessage("Shot "+target.Username+"("+strconv.Itoa(target.ID)+")"+"with a blank", clients[clientID])
} }
} }
return "SHOT"
} }
return "Error" return "Error"
} }
@ -85,11 +130,11 @@ func AcceptCommand(args []string, clientID int) string {
defer clientsMutex.Unlock() defer clientsMutex.Unlock()
matchesMutex.Lock() matchesMutex.Lock()
defer matchesMutex.Unlock() defer matchesMutex.Unlock()
if clients[clientID].match.guest == clients[clientID] { if clients[clientID].match != nil && clients[clientID].match.guest == clients[clientID] {
clients[clientID].match.round = 0
clients[clientID].match.host.sendMessage("Accepted", clients[clientID]) clients[clientID].match.host.sendMessage("Accepted", clients[clientID])
clients[clientID].isHost = false clients[clientID].isHost = false
clients[clientID].match.gun.Reload(8) clients[clientID].match.Guestturn = false
clients[clientID].match.Start()
return "Accepted" return "Accepted"
} }
return "No match to accept" return "No match to accept"
@ -98,11 +143,11 @@ func AcceptCommand(args []string, clientID int) string {
func StartCommand(args []string, clientID int) string { func StartCommand(args []string, clientID int) string {
if len(args) == 1 { if len(args) == 1 {
targetID := getByIDOrName(args[0]) targetID := getByIDOrName(args[0])
if targetID >= 0 { if targetID >= 0 && targetID != clientID && (clients[clientID].match == nil || clients[clientID].match.round <= 0) {
match := Match{ match := Match{
host: clients[clientID], host: clients[clientID],
guest: clients[targetID], guest: clients[targetID],
round: -1, round: 0,
gun: Gun{ gun: Gun{
doubled: false, doubled: false,
}, },
@ -110,11 +155,13 @@ func StartCommand(args []string, clientID int) string {
clientsMutex.Lock() clientsMutex.Lock()
clients[clientID].match = &match clients[clientID].match = &match
clients[targetID].match = &match clients[targetID].match = &match
clients[clientID].isHost = true
clients[targetID].sendMessage("Type \"accept\" to accept a match.", clients[clientID]) clients[targetID].sendMessage("Type \"accept\" to accept a match.", clients[clientID])
clientsMutex.Unlock() clientsMutex.Unlock()
matchesMutex.Lock() matchesMutex.Lock()
matches = append(matches, &match) matches = append(matches, &match)
matchesMutex.Unlock() matchesMutex.Unlock()
return "Successfully challenged"
} else { } else {
return "Unknown user" return "Unknown user"
} }
@ -122,14 +169,6 @@ func StartCommand(args []string, clientID int) string {
return "Invalid argument" return "Invalid argument"
} }
func HelpCommand(args []string, clientID int) string {
// Handle help command
if len(clients) > 0 {
return "Just help yourself, " + clients[clientID].Username
}
return "No clients available"
}
func RandomCommand(args []string, clientID int) string { func RandomCommand(args []string, clientID int) string {
// Handle help command // Handle help command
if len(args) == 2 { if len(args) == 2 {
@ -153,7 +192,7 @@ func ClearCommand(args []string, clientID int) string {
func SetNameCommand(args []string, clientID int) string { func SetNameCommand(args []string, clientID int) string {
// Handle help command // Handle help command
if len(args) == 1 { if len(args) >= 1 {
newName := args[0] newName := args[0]
clients[clientID].Username = newName clients[clientID].Username = newName
return "Name set to " + newName return "Name set to " + newName

31
gun.go

@ -8,25 +8,38 @@ type Gun struct {
} }
// fire // fire
func (g Gun) Shoot(target *Client) bool { func (g Gun) Shoot(target *Client) (int, []bool) {
shot := false shot := 2
if len(g.magazine) == 0 {
return shot, g.magazine
}
shot = 0
if g.magazine[len(g.magazine)-1] { if g.magazine[len(g.magazine)-1] {
shot = true shot = 1
if g.doubled { if g.doubled {
target.hp -= 2 target.HP -= 2
} else { } else {
target.hp -= 1 target.HP -= 1
} }
target.send("\a")
target.match.Send("")
} }
g.magazine = g.magazine[:len(g.magazine)-1]
g.magazine = g.Rack()
g.doubled = false g.doubled = false
return shot return shot, g.magazine
} }
func (g Gun) Reload(count int) { func (g Gun) Rack() []bool {
g.magazine = g.magazine[:len(g.magazine)-1]
return g.magazine
}
func (g Gun) Reload(count int) []bool {
g.magazine = make([]bool, count) g.magazine = make([]bool, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
g.magazine[i] = rand.Intn(2) == 1 g.magazine[i] = rand.Intn(6) <= 2
} }
return g.magazine
} }

11
item.go

@ -3,12 +3,15 @@ package main
type Item struct { type Item struct {
kind int kind int
match *Match match *Match
owner *Client
} }
func (i Item) name() string { func (i Item) name() string {
switch i.kind { switch i.kind {
case 0: case 0:
return "Doubler" return "Doubler"
case 1:
return "Beer"
default: default:
return "None" return "None"
} }
@ -18,5 +21,13 @@ func (i Item) useItem(param1 int) {
switch i.kind { switch i.kind {
case 0: case 0:
i.match.gun.doubled = true 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
} }
} }

171
match.go

@ -1,8 +1,179 @@
package main package main
import (
"math/rand"
"strconv"
)
type Match struct { type Match struct {
host *Client host *Client
guest *Client guest *Client
round int round int
itemCount int
gun Gun gun Gun
Guestturn bool
}
func (m *Match) LoadGun() {
switch m.round {
case 1:
m.gun.magazine = m.gun.Reload(4)
break
case 2:
m.gun.magazine = m.gun.Reload(8)
break
case 3:
m.gun.magazine = m.gun.Reload(16)
break
default:
m.gun.magazine = m.gun.Reload(1)
break
}
}
func (m *Match) Start() {
m.NextRound()
}
func (m *Match) resetHP() {
switch m.round {
case 1:
m.host.HP = 5
m.guest.HP = 5
break
case 2:
m.host.HP = 2
m.guest.HP = 2
break
case 3:
m.host.HP = 4
m.guest.HP = 4
break
default:
m.host.HP = 1
m.guest.HP = 1
break
}
}
func (m *Match) announceRounds() {
live := 0
blank := 0
for _, round := range m.gun.magazine {
if round {
live++
} else {
blank++
}
}
doubled := "Shotgun is normal"
if m.gun.doubled {
doubled = "Shotgun is 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) giveItems() {
count := 0
switch m.round {
case 2:
count = 2
break
case 3:
count = 3
break
}
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,
})
}
}
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) NextRound() bool {
defer m.announceItems()
defer m.announceRounds()
defer m.announceHP()
defer m.LoadGun()
defer m.resetHP()
defer m.giveItems()
if m.round < 3 {
m.round++
} else if m.round == 3 {
return true //final
}
return false
}
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) {
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)
} else {
m.host.sendMessage("It is your turn to shoot", m.guest)
}
}
func (m *Match) CheckStatus() {
if m.host.HP < 1 {
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)
}
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)
} else {
m.announceTurn()
}
}
if len(m.gun.magazine) == 0 {
m.NextRound()
}
} }

@ -85,13 +85,29 @@ func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet
if len(args) > 0 { if len(args) > 0 {
cmd := args[0] cmd := args[0]
internalCommand := false
if cmd == "exit" { if cmd == "exit" {
internalCommand = true
return return
} else if cmd == "help" {
internalCommand = true
commands := ""
for command, commandEntry := range handler.CommandRegistry.commands {
commands += 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)
} else {
clients[clientID].send("No clients available" + "\r\nCommands:\r\n" + commands)
}
} }
handlerFunc, ok, clientID := handler.CommandRegistry.GetCommandHandler(cmd, clientID) handlerFunc, ok, clientID := handler.CommandRegistry.GetCommandHandler(cmd, clientID)
clients[clientID].HistoryIndex = 0 clients[clientID].HistoryIndex = 0
if ok || internalCommand {
if ok { if ok {
message = handlerFunc(args[1:], clientID) message = handlerFunc(args[1:], clientID)
}
clients[clientID].History = append(clients[clientID].History, command) clients[clientID].History = append(clients[clientID].History, command)
} else { } else {
message = "Command not found" message = "Command not found"