507 lines
13 KiB
Go
507 lines
13 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
var items []item
|
|
var inventar []invPos
|
|
|
|
func checkErr(err error) {
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
var gActors = make(map[int]string)
|
|
|
|
var isInTrade int
|
|
|
|
var myTrade [16]tradeStr
|
|
var hisTrade [16]tradeStr
|
|
|
|
// read items list and create the array
|
|
func populateItems() {
|
|
file, err := os.Open("item_info.txt")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
a := strings.Split(string(scanner.Text()), "|")
|
|
id, _ := strconv.Atoi(strings.ReplaceAll(a[0], " ", ""))
|
|
text_id := strings.TrimSpace(a[1])
|
|
item_price, _ := strconv.ParseFloat(strings.TrimSpace(a[2]), 32)
|
|
n := item{Id: id, Text: text_id, Price: item_price}
|
|
items = append(items, n)
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Process incoming text messages from game
|
|
func processPmMessage(c net.Conn, user string, mesaj string) {
|
|
// fmt.Printf("PM MESSAGE command: |%s|\n", mesaj)
|
|
msg := strings.Split(mesaj, " ")[1]
|
|
switch msg {
|
|
case "#ver", "ver":
|
|
sendMessage(c, user, "GoEl bot v1 - written by mihaim")
|
|
case "#inv", "inv":
|
|
sendMessage(c, user, "Bot inventory:")
|
|
for i := 0; i < 36; i++ {
|
|
if inventar[i].Quantity != 0 {
|
|
for _, it := range items {
|
|
if inventar[i].Id == it.Id {
|
|
sendMessage(c, user, "Item: "+strconv.Itoa(inventar[i].Quantity)+" "+it.Text+" sell price: "+fmt.Sprintf("%.2f", it.Price))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case "#set":
|
|
if len(strings.Split(mesaj, " ")) != 4 {
|
|
sendMessage(c, user, "#set <item_id> <price>")
|
|
break
|
|
}
|
|
itemId, _ := strconv.ParseInt(strings.Split(mesaj, " ")[2], 0, 0)
|
|
newPrice, _ := strconv.ParseFloat(strings.Split(mesaj, " ")[3], 64)
|
|
items[itemId].Price = newPrice
|
|
|
|
a1 := fmt.Sprintf("Set price for %s to %.2f", items[itemId].Text, items[itemId].Price)
|
|
sendMessage(c, user, a1)
|
|
case "#get", "get":
|
|
if len(strings.Split(mesaj, " ")) != 3 {
|
|
sendMessage(c, user, "#get <item_id>")
|
|
break
|
|
}
|
|
itemId, _ := strconv.ParseInt(strings.Split(mesaj, " ")[2], 0, 0)
|
|
a1 := fmt.Sprintf("Price for %s is %.2f", items[itemId].Text, items[itemId].Price)
|
|
sendMessage(c, user, a1)
|
|
|
|
case "#buy", "buy":
|
|
if len(strings.Split(mesaj, " ")) < 3 {
|
|
sendMessage(c, user, "usage: #buy <quantity> <item>")
|
|
break
|
|
}
|
|
if isInTrade == 0 {
|
|
sendMessage(c, user, "You need to trade with me before buying something")
|
|
break
|
|
}
|
|
|
|
q, _ := strconv.ParseInt(strings.Split(mesaj, " ")[2], 0, 0)
|
|
search_for := strings.Join(strings.Split(mesaj, " ")[3:], " ")
|
|
|
|
// search in inventory , add to trade window
|
|
i := 0
|
|
for i = 0; i < 36; i++ {
|
|
if inventar[i].Quantity != 0 && strings.Contains(strings.ToLower(items[inventar[i].Id].Text), strings.ToLower(search_for)) {
|
|
putItemOnTrade(c, int(q), i)
|
|
sendMessage(c, user, fmt.Sprintf("Added to trade window %d %s", int(q), items[inventar[i].Id].Text))
|
|
break
|
|
}
|
|
}
|
|
case "#help", "help":
|
|
sendMessage(c, user, "GoEl trade bot v1 - written by mihaim")
|
|
sendMessage(c, user, "Available commands: #buy, #inv, #ver, #help")
|
|
|
|
case "#acc", "acc":
|
|
acceptTrade(c)
|
|
//sendMessage(c, user, "Accepting trade")
|
|
|
|
case "#dbg", "dbg":
|
|
for i := 0; i < 16; i++ {
|
|
if hisTrade[i].Quantity != 0 {
|
|
sendMessage(c, user, fmt.Sprintf("His_Trade: %s %d\n", items[hisTrade[i].Id].Text, hisTrade[i].Quantity))
|
|
}
|
|
}
|
|
for i := 0; i < 16; i++ {
|
|
if myTrade[i].Quantity != 0 {
|
|
sendMessage(c, user, fmt.Sprintf("My_Trade: %s %d\n", items[myTrade[i].Id].Text, myTrade[i].Quantity))
|
|
}
|
|
}
|
|
|
|
default:
|
|
fmt.Printf("command: |%s|", strings.Split(mesaj, " ")[1])
|
|
switch user {
|
|
case "mihaim":
|
|
sendMessage(c, user, "Sar'na conashu' mihai")
|
|
case "radu":
|
|
sendMessage(c, user, "Sa traiasca dom' shef. Dulapu raporteaza ca nu's probleme")
|
|
case "domino":
|
|
sendMessage(c, user, "Traiasca dom' sherif. Dulapu la raport . Niem problem. All good")
|
|
default:
|
|
sendMessage(c, user, "Unknown command but hello to you too . And use #help for more informations")
|
|
}
|
|
}
|
|
}
|
|
|
|
func processBuffer(c net.Conn, buffer []byte, length int) {
|
|
|
|
switch buffer[0] {
|
|
|
|
// received inventory
|
|
case HERE_YOUR_INVENTORY:
|
|
|
|
for i := 0; i < int(buffer[3]); i++ {
|
|
quantity := binary.LittleEndian.Uint32(buffer[i*10+3+3 : i*10+3+3+4])
|
|
itemId := int(buffer[i*10+3+3+3+3]) + 256*int(buffer[i*10+3+3+3+4])
|
|
pos := int(buffer[i*10+3+7])
|
|
inventar[pos].Id = itemId
|
|
inventar[pos].Quantity = int(quantity)
|
|
inventar[pos].Pos = pos
|
|
}
|
|
|
|
// Ping from server
|
|
case PING_REQUEST:
|
|
a := int(buffer[1]) + 256*int(buffer[2])
|
|
fmt.Printf("->Command: %d %d PING_REQUEST\n", buffer[0], a)
|
|
sendPingReply(c, buffer)
|
|
|
|
// text message received
|
|
case RAW_TEXT:
|
|
// trade request
|
|
if strings.Split(string(buffer[5:]), " ")[1] == "wants" {
|
|
name := strings.Split(string(buffer[5:]), " ")[0]
|
|
//sendMessage(c, name, "Wanna trade? "+fmt.Sprint(is_in_trade))
|
|
// if we have the actor ... send trade
|
|
// Oare ce dracu se intimpla daca nu exista actorul ... :D will see
|
|
|
|
for key, value := range gActors {
|
|
// sendMessage(c, name, "Wanna trade? |"+value+"|")
|
|
if strings.Split(value, " ")[0] == name {
|
|
sendTrade(c, key)
|
|
isInTrade = 1
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// normal PM message
|
|
if string(buffer[5:14]) == "[PM from " {
|
|
msg := buffer[14 : length-1]
|
|
dela := strings.Split(string(msg), ":")
|
|
|
|
pm := string(strings.Join(dela[1:], ":"))
|
|
|
|
processPmMessage(c, dela[0], pm)
|
|
break
|
|
}
|
|
|
|
if strings.Split(string(buffer[5:]), " ")[1] == "aborted" {
|
|
isInTrade = 0
|
|
resetTrade()
|
|
}
|
|
|
|
fmt.Printf("Alt mesaj |%s|\n", buffer[5:])
|
|
|
|
// add new actor
|
|
case ADD_NEW_ACTOR:
|
|
actorName := make([]byte, 30)
|
|
actorId := int(buffer[3]) + 256*int(buffer[4])
|
|
for i := 0; i < 30; i++ {
|
|
if buffer[i+20] != 0 {
|
|
actorName[i] = buffer[i+20]
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
act := strings.TrimRight(string(actorName), "\t \n\x00")
|
|
gActors[actorId] = act
|
|
fmt.Printf("NE Actor: Id: %d Name: |%s|\n", actorId, act)
|
|
|
|
// new minute
|
|
case NEW_MINUTE:
|
|
break
|
|
|
|
case GET_TRADE_OBJECT:
|
|
fmt.Printf("GET_TRADE_OBJECT ")
|
|
img := int(buffer[3]) + 256*int(buffer[4])
|
|
qua := int(buffer[5]) + 256*int(buffer[6]) + 65535*(int(buffer[7])+256*int(buffer[8]))
|
|
pos := int(buffer[7+3])
|
|
tipe := int(buffer[3+6])
|
|
us := int(buffer[11])
|
|
id := int(buffer[12]) + 256*int(buffer[13])
|
|
if us == 0 {
|
|
fmt.Printf("Noi am pus %d %s \n", qua, items[id].Text)
|
|
myTrade[pos].Id = id
|
|
myTrade[pos].ImageId = img
|
|
myTrade[pos].Quantity += qua
|
|
myTrade[pos].Type = tipe
|
|
|
|
} else {
|
|
fmt.Printf("El a pus %d %s \n", qua, items[id].Text)
|
|
hisTrade[pos].Id = id
|
|
hisTrade[pos].ImageId = img
|
|
hisTrade[pos].Quantity += qua
|
|
hisTrade[pos].Type = tipe
|
|
}
|
|
|
|
for i := 0; i < length-3; i++ {
|
|
fmt.Printf("%2x ", buffer[i+3])
|
|
}
|
|
fmt.Printf("\n")
|
|
|
|
case REMOVE_TRADE_OBJECT:
|
|
fmt.Printf("REMOVE_TRADE_OBJECT ")
|
|
qua := int(buffer[3]) + 256*int(buffer[4]) + 65535*(int(buffer[5])+256*int(buffer[6]))
|
|
pos := int(buffer[3+4])
|
|
us := int(buffer[3+5])
|
|
if us == 0 {
|
|
fmt.Printf("Noi am retras %d %s \n", qua, items[myTrade[pos].Id].Text)
|
|
myTrade[pos].Quantity -= qua
|
|
} else {
|
|
fmt.Printf("El a retras %d %s \n", qua, items[hisTrade[pos].Id].Text)
|
|
hisTrade[pos].Quantity -= qua
|
|
}
|
|
|
|
for i := 0; i < length-3; i++ {
|
|
fmt.Printf("%2x ", buffer[i+3])
|
|
}
|
|
fmt.Printf("\n")
|
|
|
|
case GET_TRADE_ACCEPT:
|
|
// fmt.Printf("GET_TRADE ACCEPT %02x %02x %02x %02x\n", buffer[0], buffer[1], buffer[2], buffer[3])
|
|
if buffer[3] == 0 {
|
|
break
|
|
}
|
|
trade_value := 0.0
|
|
for i := 0; i < 16; i++ {
|
|
trade_value += float64(myTrade[i].Quantity) * float64(items[myTrade[i].Id].Price)
|
|
}
|
|
|
|
fmt.Printf("Total trade value %d\n", int(trade_value))
|
|
for i := 0; i < 16; i++ {
|
|
//fmt.Printf("GET_TRADE_ACCEPT: index %d valoare = %d %d %d \n", i, his_trade[i].Id, his_trade[i].Quantity, int(trade_value))
|
|
if hisTrade[i].Id == MONEY && int(hisTrade[i].Quantity) == int(trade_value) {
|
|
//fmt.Printf("GET_TRADE_ACCEPT: valoare = %d %d \n", his_trade[i].Quantity, int(trade_value))
|
|
acceptTrade(c)
|
|
break
|
|
}
|
|
}
|
|
//fmt.Printf("GET_TRADE_ACCEPT: valoare = %d \n", int(trade_value))
|
|
|
|
case GET_TRADE_REJECT:
|
|
fmt.Printf("GET_TRADE REJECT\n")
|
|
case GET_TRADE_EXIT:
|
|
fmt.Printf("GET_TRADE EXIT\n")
|
|
|
|
// get partial stat
|
|
case SEND_PARTIAL_STAT:
|
|
// fmt.Printf("get_partial_stat from server")
|
|
break
|
|
// add new enhanced actor
|
|
case ADD_NEW_ENHANCED_ACTOR:
|
|
actorName := make([]byte, 30)
|
|
actorId := int(buffer[3]) + 256*int(buffer[4])
|
|
for i := 0; i < 30; i++ {
|
|
if buffer[i+31] != 0 {
|
|
actorName[i] = buffer[i+31]
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
act := strings.TrimRight(string(actorName), "\t \n\x00")
|
|
gActors[actorId] = act
|
|
fmt.Printf("Actor: Id: %d Name: |%s|\n", actorId, act)
|
|
|
|
// remove actor
|
|
case REMOVE_ACTOR:
|
|
actorId := int(buffer[3]) + 256*int(buffer[4])
|
|
delete(gActors, actorId)
|
|
fmt.Printf("Remove Actor Id: %d \n", actorId)
|
|
|
|
// add actor command ( do nothing with this )
|
|
case ADD_ACTOR_COMMAND:
|
|
break
|
|
|
|
// everything else ( commands not implemented yet)
|
|
default:
|
|
a := int(buffer[1]) + 256*int(buffer[2])
|
|
fmt.Printf("->Command: %d %d\n", buffer[0], a)
|
|
fmt.Printf("->: %s \n", buffer[3:])
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
|
|
isInTrade = 0
|
|
resetTrade()
|
|
viper.SetConfigName("config")
|
|
viper.AddConfigPath(".")
|
|
viper.AutomaticEnv()
|
|
viper.SetConfigType("yml")
|
|
var configuration configurations
|
|
|
|
populateItems()
|
|
|
|
// Start adding web interface and handlers ( for now listen on 8080 . move it to config.yml in the end)
|
|
http.HandleFunc("/hello", webHello)
|
|
http.HandleFunc("/", webSlash)
|
|
http.HandleFunc("/inv", webShowInventory)
|
|
http.HandleFunc("/save", saveItemsAndPrices)
|
|
|
|
http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
|
|
tmpl := template.Must(template.ParseFiles("templates/test.tmpl"))
|
|
data := webPageData{
|
|
PageTitle: "Items list and prices",
|
|
Ite: items,
|
|
}
|
|
tmpl.Execute(w, data)
|
|
})
|
|
|
|
go func() {
|
|
fmt.Println("listen on 8082")
|
|
log.Fatal(http.ListenAndServe(":8082", nil))
|
|
}()
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
fmt.Printf("Error reading config file, %s", err)
|
|
}
|
|
|
|
if err := viper.Unmarshal(&configuration); err != nil {
|
|
fmt.Printf("Unable to decode into struct, %s", err)
|
|
}
|
|
|
|
c, err := net.Dial("tcp", configuration.Server.Ip+":"+fmt.Sprint(configuration.Server.Port))
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
// initial inventory is empty
|
|
for i := 0; i < 36; i++ {
|
|
var v1 = new(invPos)
|
|
v1.Id = 0
|
|
v1.Quantity = 0
|
|
v1.Pos = 0
|
|
inventar = append(inventar, *v1)
|
|
}
|
|
|
|
sendLogin(c, configuration.Credential.User, configuration.Credential.Password)
|
|
|
|
sec := time.Now().Unix()
|
|
|
|
// main loop for communicating with el server
|
|
|
|
// One thread to rule them all :P ( and keep us connected)
|
|
go func() {
|
|
for {
|
|
if time.Now().Unix()-sec > 30 {
|
|
sendHeartBeat(c)
|
|
sec = time.Now().Unix()
|
|
fmt.Printf(time.Now().Format("2006-01-02 15:04:05") + "Avoid lagging out. Sending heartbeat pack\n")
|
|
}
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
}()
|
|
|
|
for {
|
|
reply := make([]byte, 4096)
|
|
length, err := c.Read(reply)
|
|
|
|
checkErr(err)
|
|
|
|
//fmt.Printf("Received command: %d and length %d \n", reply[0], length)
|
|
//fmt.Printf("Hex dump: %s\n", hex.Dump(reply))
|
|
|
|
indexul := 0
|
|
for ok := true; ok; ok = (indexul+int(reply[indexul+1])+256*int(reply[indexul+2]) < length) {
|
|
packLen := int(reply[indexul+1]) + 256*int(reply[indexul+2])
|
|
if packLen >= 2 {
|
|
process := reply[indexul : indexul+packLen+2]
|
|
// fmt.Printf("->Command imbinata: %d %s\n", process[0], process[3:])
|
|
processBuffer(c, process, len(process))
|
|
|
|
}
|
|
indexul = indexul + int(reply[indexul+1]) + 256*int(reply[indexul+2]) + 2
|
|
}
|
|
}
|
|
}
|
|
|
|
func webHello(w http.ResponseWriter, req *http.Request) {
|
|
fmt.Fprintf(w, "hello\n")
|
|
// testing modify variable from web
|
|
isInTrade++
|
|
}
|
|
|
|
func webSlash(w http.ResponseWriter, req *http.Request) {
|
|
tmpl := template.Must(template.ParseFiles("templates/home.tmpl"))
|
|
data := webPageData{
|
|
PageTitle: "Home page",
|
|
Ite: nil,
|
|
}
|
|
tmpl.Execute(w, data)
|
|
}
|
|
|
|
func webShowInventory(w http.ResponseWriter, req *http.Request) {
|
|
var inv_full []item
|
|
|
|
// Create structure to be rendered in webpage
|
|
for i := 0; i < 36; i++ {
|
|
if inventar[i].Quantity != 0 {
|
|
for _, it := range items {
|
|
if inventar[i].Id == it.Id {
|
|
|
|
n := item{Id: it.Id, Text: it.Text, Price: it.Price, Quantity: inventar[i].Quantity}
|
|
inv_full = append(inv_full, n)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tmpl := template.Must(template.ParseFiles("templates/inv.tmpl"))
|
|
|
|
tmpl.Execute(w, inv_full)
|
|
}
|
|
|
|
func saveItemsAndPrices(w http.ResponseWriter, req *http.Request) {
|
|
// a test
|
|
// items[0].Price++
|
|
|
|
file, err := os.OpenFile("item_info.txt", os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
datawriter := bufio.NewWriter(file)
|
|
|
|
for _, data := range items {
|
|
_, _ = datawriter.WriteString(fmt.Sprintf("%d | %s | %.2f\n", data.Id, data.Text, data.Price))
|
|
}
|
|
|
|
datawriter.Flush()
|
|
}
|
|
|
|
func resetTrade() {
|
|
fmt.Printf("Reset Trade\n")
|
|
|
|
for i := 0; i < 16; i++ {
|
|
myTrade[i].Id = 0
|
|
myTrade[i].ImageId = 0
|
|
myTrade[i].Quantity = 0
|
|
myTrade[i].Type = 0
|
|
hisTrade[i].Id = 0
|
|
hisTrade[i].ImageId = 0
|
|
hisTrade[i].Quantity = 0
|
|
hisTrade[i].Type = 0
|
|
}
|
|
}
|