This commit is contained in:
Bruno Rybársky 2024-06-09 11:02:18 +02:00
parent 4e195c6ba3
commit bf471b4934
10 changed files with 420 additions and 236 deletions

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="GoContextTodo" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="GoUnnecessarilyExportedIdentifiers" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
</profile>
</component>

@ -1,2 +1,3 @@
# telnetroulette # Telnet roulette
# Just like Buckshot Roulette, but in telnet

@ -2,10 +2,12 @@ package main
import ( import (
"github.com/BRNSystems/go-telnet" "github.com/BRNSystems/go-telnet"
"math/rand"
"strconv" "strconv"
"strings"
) )
type Client struct { type client struct {
Client *telnet.Client Client *telnet.Client
Username string Username string
ID int ID int
@ -14,15 +16,15 @@ type Client struct {
context *telnet.Context context *telnet.Context
writer *telnet.Writer writer *telnet.Writer
reader *telnet.Reader reader *telnet.Reader
match *Match match *match
isHost bool isHost bool
HP int HP int
items []Item items []item
// Add more fields as needed // Add more fields as needed
} }
func NewClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Reader) int { func newClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Reader) int {
client := Client{} client := client{}
clientsMutex.Lock() clientsMutex.Lock()
clients = append(clients, &client) clients = append(clients, &client)
@ -39,19 +41,22 @@ func NewClient(context *telnet.Context, writer *telnet.Writer, reader *telnet.Re
return clientID return clientID
} }
func RemoveClient(clientID int) { func removeClient(clientID int) {
clientsMutex.Lock() clientsMutex.Lock()
defer clientsMutex.Unlock() defer clientsMutex.Unlock()
clients = append(clients[:clientID], clients[clientID+1:]...) clients = append(clients[:clientID], clients[clientID+1:]...)
} }
func (client Client) send(message string) { func (client *client) send(message string) {
writer := *(client.writer) writer := *(client.writer)
writer.Write([]byte(message)) _, err := writer.Write([]byte(message))
if err != nil {
return
}
} }
func (client Client) sendMessage(message string, sender *Client) { func (client *client) sendMessage(message string, sender *client) {
client.send("\a\r\n<" + sender.Username + "(" + strconv.Itoa(sender.ID) + ")> " + message + "\r\n") client.send("\a\r\n<" + sender.getMentionName() + "> " + message + "\r\n")
} }
func getIDByName(name string) int { func getIDByName(name string) int {
@ -77,3 +82,41 @@ func getByIDOrName(by string) int {
} }
return -1 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,
})
}

@ -7,34 +7,34 @@ import (
"sync" "sync"
) )
type Command struct { type command struct {
commandHandlerFunc CommandHandlerFunc commandHandlerFunc commandHandlerFunc
description string 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]Command 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]Command), 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() cr.mutex.Lock()
defer cr.mutex.Unlock() defer cr.mutex.Unlock()
cr.commands[command] = Command{ cr.commands[commandName] = command{
commandHandlerFunc: handler, commandHandlerFunc: handler,
description: desc, 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()
commandEntry, ok := cr.commands[command] commandEntry, ok := cr.commands[command]
@ -42,20 +42,33 @@ func (cr *CommandRegistry) GetCommandHandler(command string, clientID int) (Comm
return handler, ok, clientID return handler, ok, clientID
} }
func RegisterCommands(registry *CommandRegistry) { func registerCommands(registry *commandRegistry) {
registry.RegisterCommand("clear", ClearCommand, `Clears your screen (clear)`) registry.registerCommand("clear", clearCommand, `Clears your screen (clear)`)
registry.RegisterCommand("setname", SetNameCommand, `Sets your name (setname yourname[string])`) registry.registerCommand("setname", setNameCommand, `Sets your name (setname yourname[string])`)
registry.RegisterCommand("list", ListClientsCommand, `Lists clients (list)`) 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("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("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("challenge", startCommand, `Challenge a client (challenge client[id or string{username}])`)
registry.RegisterCommand("accept", AcceptCommand, `Accepts a challenge (accept)`) 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("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("useitem", useItemCommand, `Uses an item in a match (useitem index[number])`)
registry.registerCommand("status", statusCommand, `Prints the game status (status)`)
// Add more commands here... // 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 param := -1
itemID := -1 itemID := -1
if len(args) >= 2 { if len(args) >= 2 {
@ -73,59 +86,65 @@ func UseItemCommand(args []string, clientID int) string {
} }
} }
if itemID >= 0 && itemID < clients[clientID].match.itemCount { 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].items[itemID].useItem(param)
clients[clientID].match.SendMessage("\r\nUsing item "+clients[clientID].items[itemID].name(), clients[clientID])
return "Used" return "Used"
} else { } else {
return "ERROR using item" return "ERROR using item"
} }
} }
func ShootCommand(args []string, clientID int) string { func shootCommand(args []string, clientID int) string {
if clients[clientID].match != nil && clients[clientID].match.round > 0 && clients[clientID].match.Guestturn != clients[clientID].isHost { if clients[clientID].match != nil && clients[clientID].match.round > 0 {
defer clients[clientID].match.CheckStatus() if clients[clientID].match.guestsTurn != clients[clientID].isHost {
defer clients[clientID].match.announceHP() defer clients[clientID].match.checkStatus()
defer clients[clientID].match.announceRounds() defer clients[clientID].match.announceHP()
self := false defer clients[clientID].match.announceRounds()
if len(args) == 1 { self := false
if strings.ToLower(args[0]) == "self" { if len(args) == 1 {
self = true if strings.ToLower(args[0]) == "self" {
} else if getByIDOrName(args[0]) == clientID { self = true
self = true } else if getByIDOrName(args[0]) == clientID {
self = true
}
} }
} var fired int
var fired int var target *client
var target *Client if self {
if self { target = clients[clientID]
target = clients[clientID] fired = clients[clientID].match.gun.shoot(target)
fired, clients[clientID].match.gun.magazine = clients[clientID].match.gun.Shoot(target) if fired == 1 {
if fired == 1 { clients[clientID].match.guestsTurn = clients[clientID].isHost
clients[clientID].match.Guestturn = clients[clientID].isHost clients[clientID].match.sendMessage("Shot myself with a live", clients[clientID])
clients[clientID].match.SendMessage("Shot myself with a live", clients[clientID]) } else {
clients[clientID].match.sendMessage("Shot myself with a blank", clients[clientID])
}
} else { } 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 { } else {
if clients[clientID].isHost { return "It is not your turn"
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 "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() clientsMutex.Lock()
defer clientsMutex.Unlock() defer clientsMutex.Unlock()
matchesMutex.Lock() matchesMutex.Lock()
@ -133,28 +152,29 @@ func AcceptCommand(args []string, clientID int) string {
if clients[clientID].match != nil && clients[clientID].match.guest == clients[clientID] { if clients[clientID].match != nil && clients[clientID].match.guest == clients[clientID] {
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.Guestturn = false clients[clientID].match.guestsTurn = false
clients[clientID].match.Start() clients[clientID].match.start()
return "Accepted" return "Accepted"
} }
return "No match to accept" return "No match to accept"
} }
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 && targetID != clientID && (clients[clientID].match == nil || clients[clientID].match.round <= 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: 0, round: 0,
gun: Gun{ gun: gun{
doubled: false, doubled: false,
}, },
} }
clientsMutex.Lock() clientsMutex.Lock()
clients[clientID].match = &match clients[clientID].match = &match
clients[targetID].match = &match clients[targetID].match = &match
match.itemCount = 8
clients[clientID].isHost = true 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()
@ -169,7 +189,7 @@ func StartCommand(args []string, clientID int) string {
return "Invalid argument" return "Invalid argument"
} }
func RandomCommand(args []string, clientID int) string { func randomCommand(args []string, _ int) string {
// Handle help command // Handle help command
if len(args) == 2 { if len(args) == 2 {
from, err1 := strconv.Atoi(args[0]) from, err1 := strconv.Atoi(args[0])
@ -185,12 +205,12 @@ func RandomCommand(args []string, clientID int) string {
return "No range provided" return "No range provided"
} }
func ClearCommand(args []string, clientID int) string { func clearCommand(_ []string, _ int) string {
// Handle help command // Handle help command
return "\033[H\033[2J\033[K" return "\033[H\033[2J\033[K"
} }
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]
@ -200,28 +220,28 @@ func SetNameCommand(args []string, clientID int) string {
return "No name provided" return "No name provided"
} }
func ListClientsCommand(args []string, clientID int) string { func listClientsCommand(_ []string, _ int) string {
message := "" var message strings.Builder
for i, client := range clients { 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" message := "Message not delivered"
if len(args) >= 2 { if len(args) >= 2 {
toClientID := getByIDOrName(args[0]) toClientID := getByIDOrName(args[0])
if toClientID >= 0 { if toClientID >= 0 {
toClientName := clients[toClientID].Username toClientName := clients[toClientID].Username
newMessage := "" var newMessage strings.Builder
//all the remaining arguments are a part of the message, join them with space //all the remaining arguments are a part of the message, join them with space
for i, arg := range args { for i, arg := range args {
if i > 0 { 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 return "Message sent to " + toClientName
} else { } else {
message = "Invalid recipient" message = "Invalid recipient"

22
gun.go

@ -2,16 +2,15 @@ package main
import "math/rand" import "math/rand"
type Gun struct { type gun struct {
doubled bool doubled bool
magazine []bool magazine []bool
} }
// fire func (g *gun) shoot(target *client) int {
func (g Gun) Shoot(target *Client) (int, []bool) {
shot := 2 shot := 2
if len(g.magazine) == 0 { if len(g.magazine) == 0 {
return shot, g.magazine return shot
} }
shot = 0 shot = 0
if g.magazine[len(g.magazine)-1] { if g.magazine[len(g.magazine)-1] {
@ -21,25 +20,24 @@ func (g Gun) Shoot(target *Client) (int, []bool) {
} else { } else {
target.HP -= 1 target.HP -= 1
} }
target.send("\a") target.send("\a\a")
target.match.Send("") target.match.send("")
} }
g.magazine = g.Rack() g.rack()
g.doubled = false 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] 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) g.magazine = make([]bool, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
g.magazine[i] = rand.Intn(6) <= 2 g.magazine[i] = rand.Int63()&(1<<62) == 0
} }
return g.magazine return g.magazine
} }

143
item.go

@ -1,33 +1,144 @@
package main package main
type Item struct { import (
"math"
"math/rand"
"strconv"
)
type item struct {
kind int kind int
match *Match match *match
owner *Client 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 "Saw"
case 1: case 1:
return "Beer" 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: default:
return "None" return "None"
} }
} }
func (i Item) useItem(param1 int) { func (i *item) useItem(param1 int) {
switch i.kind { successfully := false
case 0: if i.owner.isHost != i.owner.match.guestsTurn {
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
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()
} }
} }

10
main.go

@ -5,15 +5,15 @@ import (
"sync" "sync"
) )
var clients []*Client var clients []*client
var clientsMutex sync.Mutex var clientsMutex sync.Mutex
var matches []*Match var matches []*match
var matchesMutex sync.Mutex var matchesMutex sync.Mutex
func main() { func main() {
addr := ":6969" addr := ":6969"
commandRegistry := NewCommandRegistry() commandRegistry := newCommandRegistry()
termHandler := NewInternalTerminalHandler("# ", termHandler := newInternalTerminalHandler("# ",
` ____ _ _ ____ _ _ _ ` ____ _ _ ____ _ _ _
/ ___|| |__ ___ | |_ __ _ _ _ _ __ | _ \ ___ _ _| | ___| |_| |_ ___ / ___|| |__ ___ | |_ __ _ _ _ _ __ | _ \ ___ _ _| | ___| |_| |_ ___
\___ \| '_ \ / _ \| __/ _`+"`"+` | | | | '_ \ | |_) / _ \| | | | |/ _ \ __| __/ _ \ \___ \| '_ \ / _ \| __/ _`+"`"+` | | | | '_ \ | |_) / _ \| | | | |/ _ \ __| __/ _ \
@ -23,7 +23,7 @@ func main() {
commandRegistry) commandRegistry)
// Register commands // Register commands
RegisterCommands(commandRegistry) registerCommands(commandRegistry)
if err := telnet.ListenAndServe(addr, termHandler); nil != err { if err := telnet.ListenAndServe(addr, termHandler); nil != err {
panic(err) panic(err)

128
match.go

@ -1,62 +1,59 @@
package main package main
import ( import (
"math/rand"
"strconv" "strconv"
) )
type Match struct { type match struct {
host *Client host *client
guest *Client guest *client
round int round int
itemCount int maxHP int
gun Gun itemCount int
Guestturn bool gun gun
guestsTurn bool
} }
func (m *Match) LoadGun() { func (m *match) loadGun() {
switch m.round { switch m.round {
case 1: case 1:
m.gun.magazine = m.gun.Reload(4) m.gun.magazine = m.gun.reload(4)
break break
case 2: case 2:
m.gun.magazine = m.gun.Reload(8) m.gun.magazine = m.gun.reload(8)
break break
case 3: case 3:
m.gun.magazine = m.gun.Reload(16) m.gun.magazine = m.gun.reload(16)
break break
default: default:
m.gun.magazine = m.gun.Reload(1) m.gun.magazine = m.gun.reload(1)
break break
} }
} }
func (m *Match) Start() { func (m *match) start() {
m.NextRound() m.nextRound()
} }
func (m *Match) resetHP() { func (m *match) resetHP() {
HP := 1
switch m.round { switch m.round {
case 1: case 1:
m.host.HP = 5 HP = 5
m.guest.HP = 5
break break
case 2: case 2:
m.host.HP = 2 HP = 2
m.guest.HP = 2
break break
case 3: case 3:
m.host.HP = 4 HP = 4
m.guest.HP = 4
break
default:
m.host.HP = 1
m.guest.HP = 1
break break
} }
m.host.HP = HP
m.guest.HP = HP
m.maxHP = HP
} }
func (m *Match) announceRounds() { func (m *match) announceRounds() {
live := 0 live := 0
blank := 0 blank := 0
for _, round := range m.gun.magazine { for _, round := range m.gun.magazine {
@ -70,19 +67,19 @@ func (m *Match) announceRounds() {
doubled := "Shotgun is normal" doubled := "Shotgun is normal"
if m.gun.doubled { 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() { func (m *match) announceHP() {
m.Send("\r\nHPs are:\r\n" + m.send("\r\nHPs are:\r\n" +
m.host.Username + "(" + strconv.Itoa(m.host.ID) + ")" + " - " + strconv.Itoa(m.host.HP) + "\r\n" + m.host.getMentionName() + " - " + m.host.renderHP() + "\r\n" +
m.guest.Username + "(" + strconv.Itoa(m.guest.ID) + ")" + " - " + strconv.Itoa(m.guest.HP)) m.guest.getMentionName() + " - " + m.guest.renderHP())
} }
func (m *Match) giveItems() { func (m *match) giveItems() {
count := 0 count := 0
switch m.round { switch m.round {
case 2: case 2:
@ -94,42 +91,25 @@ func (m *Match) giveItems() {
} }
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
m.host.items = append(m.host.items, Item{ m.host.giveItem()
kind: rand.Intn(m.itemCount), //currently only item 0 exists m.guest.giveItem()
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() { func (m *match) announceItems() {
out := "" m.sendMessage(m.host.renderItems(), m.host)
for i := 0; i < len(m.host.items); i++ { m.sendMessage(m.guest.renderItems(), m.guest)
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 { func (m *match) nextRound() bool {
defer m.announceTurn()
defer m.announceItems() defer m.announceItems()
defer m.announceRounds() defer m.announceRounds()
defer m.announceHP() defer m.announceHP()
defer m.LoadGun() defer m.loadGun()
defer m.resetHP() defer m.resetHP()
defer m.giveItems() defer m.giveItems()
m.guestsTurn = false
if m.round < 3 { if m.round < 3 {
m.round++ m.round++
} else if m.round == 3 { } else if m.round == 3 {
@ -138,42 +118,42 @@ func (m *Match) NextRound() bool {
return false return false
} }
func (m *Match) Send(message string) { func (m *match) send(message string) {
m.host.sendMessage(message, m.host) m.host.sendMessage(message, m.host)
m.guest.sendMessage(message, m.guest) 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.host.sendMessage(message, sender)
m.guest.sendMessage(message, sender) m.guest.sendMessage(message, sender)
} }
func (m *Match) announceTurn() { func (m *match) announceTurn() {
if m.Guestturn { if m.guestsTurn {
m.guest.sendMessage("It is your turn to shoot", m.host) m.guest.sendMessage("It is your turn\a", m.host)
} else { } 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 { if m.host.HP < 1 {
m.SendMessage("I died", m.host) m.sendMessage("I died", m.host)
m.SendMessage("I won", m.guest) m.sendMessage("I won", m.guest)
} }
if m.guest.HP < 1 { if m.guest.HP < 1 {
m.SendMessage("I died", m.guest) m.sendMessage("I died", m.guest)
m.SendMessage("I won", m.host) m.sendMessage("I won", m.host)
} }
if m.host.HP > 0 && m.guest.HP > 0 { if m.host.HP > 0 && m.guest.HP > 0 {
if m.round >= 3 { if m.round >= 3 {
m.SendMessage("I won", m.host) m.sendMessage("I won", m.host)
m.SendMessage("I won", m.guest) m.sendMessage("I won", m.guest)
} else { } else {
m.announceTurn() m.announceTurn()
} }
} }
if len(m.gun.magazine) == 0 { if len(m.gun.magazine) == 0 {
m.NextRound() m.nextRound()
} }
} }

@ -6,29 +6,48 @@ import (
"strings" "strings"
) )
type InternalTerminalHandler struct { type internalTerminalHandler struct {
ShellCharacter string ShellCharacter string
WelcomeMessage string WelcomeMessage string
CommandRegistry *CommandRegistry CommandRegistry *commandRegistry
} }
func NewInternalTerminalHandler(shellCharacter, welcomeMessage string, commandRegistry *CommandRegistry) *InternalTerminalHandler { func newInternalTerminalHandler(shellCharacter, welcomeMessage string, commandRegistry *commandRegistry) *internalTerminalHandler {
return &InternalTerminalHandler{ return &internalTerminalHandler{
ShellCharacter: shellCharacter, ShellCharacter: shellCharacter,
WelcomeMessage: welcomeMessage, WelcomeMessage: welcomeMessage,
CommandRegistry: commandRegistry, CommandRegistry: commandRegistry,
} }
} }
func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet.Writer, r telnet.Reader) { func clearLine(w telnet.Writer) {
clientID := NewClient(&ctx, &w, &r) _, err := w.RawWrite([]byte{0x1B, 0x5B, 0x4B})
if err != nil {
return
}
}
defer RemoveClient(clientID) func (handler *internalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet.Writer, r telnet.Reader) {
w.RawWrite([]byte{0xff, 0xfb, 0x03}) //send char by char clientID := newClient(&ctx, &w, &r)
w.RawWrite([]byte{0xff, 0xfb, 0x01}) //echo from server
w.Write([]byte(strings.ReplaceAll(handler.WelcomeMessage, "\n", "\r\n") + "\r\n")) 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 //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 var lineBuffer []byte
@ -56,8 +75,11 @@ func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet
clients[clientID].HistoryIndex++ clients[clientID].HistoryIndex++
} }
lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex]) lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex])
w.RawWrite([]byte{0x1B, 0x5B, 0x4B}) //clear line clearLine(w)
w.Write(lineBuffer) _, err := w.Write(lineBuffer)
if err != nil {
return
}
} }
} else if CSISequence == 2 && b == 'B' { } else if CSISequence == 2 && b == 'B' {
if len(lineBuffer) == 0 && len(clients[clientID].History) > 0 { 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-- clients[clientID].HistoryIndex--
} }
lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex]) lineBuffer = []byte(clients[clientID].History[clients[clientID].HistoryIndex])
w.RawWrite([]byte{0x1B, 0x5B, 0x4B}) //clear line clearLine(w)
w.Write(lineBuffer) _, err := w.Write(lineBuffer)
if err != nil {
return
}
} }
} else if b == 0x7F { } else if b == 0x7F {
if len(lineBuffer) > 0 { 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] lineBuffer = lineBuffer[:len(lineBuffer)-1]
} }
} else if b != '\r' && b != '\n' && b != 0 { } else if b != '\r' && b != '\n' && b != 0 {
CSISequence = 0 CSISequence = 0
lineBuffer = append(lineBuffer, b) lineBuffer = append(lineBuffer, b)
w.RawWrite([]byte{b}) _, err := w.RawWrite([]byte{b})
if err != nil {
return
}
} else if b == '\r' { } else if b == '\r' {
w.Write([]byte("\r\n")) _, err := w.Write([]byte("\r\n"))
if err != nil {
return
}
command := string(lineBuffer) command := string(lineBuffer)
args := strings.Fields(command) args := strings.Fields(command)
var message string var message strings.Builder
if len(args) > 0 { if len(args) > 0 {
cmd := args[0] cmd := args[0]
@ -91,32 +125,35 @@ func (handler *InternalTerminalHandler) ServeTELNET(ctx telnet.Context, w telnet
return return
} else if cmd == "help" { } else if cmd == "help" {
internalCommand = true internalCommand = true
commands := "" var commands strings.Builder
for command, commandEntry := range handler.CommandRegistry.commands { for command, commandEntry := range handler.CommandRegistry.commands {
commands += command + " - " + commandEntry.description + "\r\n" commands.WriteString(command + " - " + commandEntry.description + "\r\n")
} }
// Handle help command // Handle help command
if len(clients) > 0 { 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 { } 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 clients[clientID].HistoryIndex = 0
if ok || internalCommand { if ok || internalCommand {
if ok { if ok {
message = handlerFunc(args[1:], clientID) message.WriteString(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.WriteString("Command not found")
} }
message += "\n" + handler.ShellCharacter message.WriteString("\n" + handler.ShellCharacter)
} else { } 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 lineBuffer = nil
} }
} }

13
util.go

@ -1,14 +1 @@
package main package main
import (
"unicode"
)
func isInt(s string) bool {
for _, c := range s {
if !unicode.IsDigit(c) {
return false
}
}
return true
}