// List items in Player's inventory func Action_Inventory(gs *GameServer, cp *ClientPacket) { plobj := cp.Client.Player inv := plobj.GetSubObjects().Chan() if len(inv) == 0 { cp.Reply(gnet.NewPacket("Rchat", "You have 0 items.")) } else { counts := make(map[string]int) for sub := range inv { n := sub.GetName() if _, ok := counts[n]; ok { counts[n]++ } else { counts[n] = 1 } } for n, c := range counts { if c == 1 { cp.Reply(gnet.NewPacket("Rchat", fmt.Sprintf("You have a %s.", n))) } else { cp.Reply(gnet.NewPacket("Rchat", fmt.Sprintf("You have %d %ss.", c, n))) } } } }
// handle per-client packets func (ws *WorldSession) ReceiveProc() { defer func() { if err := recover(); err != nil { log.Printf("WorldSession: ReceiveProc: %s", err) } }() for x := range ws.ClientRChan { p, ok := x.(*gnet.Packet) if !ok { log.Printf("WorldSession: ReceiveProc: bad packet %#v from %s", x, ws.Con.RemoteAddr()) continue } cp := &ClientPacket{ws, p} ws.World.PacketChan <- cp } dis := &ClientPacket{ws, gnet.NewPacket("Tdisconnect", nil)} ws.World.PacketChan <- dis log.Printf("WorldSession: ReceiveProc: Channel closed %s", ws) }
// Player drops the item indicated by the ID from their inventory // TODO: this drops all items right now. make it drop individual items func Action_ItemDrop(gs *GameServer, cp *ClientPacket) { p := cp.Client.Player for sub := range p.GetSubObjects().Chan() { log.Printf("GameServer: Action_ItemDrop: %s dropping %s", p, sub) // remove item from player p.RemoveSubObject(sub) // put it where the player was sub.SetPos(p.GetPos()) // make it visible sub.SetTag("visible", true) sub.SetTag("gettable", true) // update clients with the new state of this object gs.SendPacketAll(gnet.NewPacket("Raction", sub)) cp.Reply(gnet.NewPacket("Rchat", fmt.Sprintf("You drop a %s.", sub.GetName()))) } }
// Prevent User from re-adding / picking up item // Disassociate item with map after action successful func Action_ItemPickup(gs *GameServer, cp *ClientPacket) { p := cp.Client.Player // we assume our cp.Data is a game.Action of type ACTION_ITEM_PICKUP // act accordingly for o := range gs.Objects.Chan() { // if same pos.. and gettable if game.SamePos(o, p) && o.GetTag("gettable") { // pickup item. log.Printf("GameServer: Action_ItemPickup: %s picking up %s", p, o) o.SetTag("visible", false) o.SetTag("gettable", false) o.SetPos(0, 0) p.AddSubObject(o) // update clients with the new state of this object gs.SendPacketAll(gnet.NewPacket("Raction", o)) cp.Reply(gnet.NewPacket("Rchat", fmt.Sprintf("You pick up a %s.", o.GetName()))) } } }
func (c *ChatPanel) HandleInput(ev termbox.Event) { c.m.Lock() defer c.m.Unlock() switch ev.Type { case termbox.EventKey: if ev.Ch != 0 { c.WriteRune(ev.Ch) } else { switch ev.Key { case termbox.KeySpace: // just add a space c.WriteRune(' ') case termbox.KeyBackspace: fallthrough case termbox.KeyBackspace2: // on backspace, remove the last rune in the buffer if c.Len() > 0 { _, size := utf8.DecodeLastRune(c.Bytes()) c.Truncate(c.Len() - size) } case termbox.KeyCtrlU: // clear the buffer, like a UNIX terminal c.Reset() case termbox.KeyEnter: // input confirmed, send it if c.Len() > 0 { c.g.SendPacket(gnet.NewPacket("Tchat", c.String())) c.Reset() c.term.SetInputHandler(nil) } case termbox.KeyEsc: // input cancelled c.Reset() c.term.SetInputHandler(nil) } } case termbox.EventResize: w, h := termbox.Size() r := image.Rect(w-1, h-2, w/2, h-1) c.Buffered = panel.NewBuffered(r, termbox.Cell{'s', termbox.ColorGreen, 0}) } }
// Top level handler for Taction packets func (gs *GameServer) HandleActionPacket(cp *ClientPacket) { action := cp.Data.(game.Action) p := cp.Client.Player _, isdir := game.DirTable[action] if isdir { gs.HandleMovementPacket(cp) } // check if this action is in our Actions table, if so execute it if f, ok := Actions[action]; ok { f(gs, cp) } gs.SendPacketAll(gnet.NewPacket("Raction", p)) }
// deal with gnet.Packets received from the server func (g *Game) HandlePacket(pk *gnet.Packet) { defer func() { if err := recover(); err != nil { log.Printf("Game: HandlePacket: %s", err) } }() log.Printf("Game: HandlePacket: %s", pk) switch pk.Tag { // Rchat: we got a text message case "Rchat": chatline := pk.Data.(string) io.WriteString(g.logpanel, chatline) // Raction: something moved on the server // Need to update the objects (sync client w/ srv) case "Raction": robj := pk.Data.(game.Object) // remote object for o := range g.Objects.Chan() { if o.GetID() == robj.GetID() { o.SetPos(robj.GetPos()) } /*else if o.GetTag("item") { item := g.Objects.FindObjectByID(o.GetID()) if item.GetTag("gettable") { item.SetPos(o.GetPos()) } else { g.Objects.RemoveObject(item) } } */ } // Rnewobject: new object we need to track case "Rnewobject": obj := pk.Data.(game.Object) g.Objects.Add(obj) // Rdelobject: some object went away case "Rdelobject": obj := pk.Data.(game.Object) g.Objects.RemoveObject(obj) // Rgetplayer: find out who we control case "Rgetplayer": playerid := pk.Data.(int) pl := g.Objects.FindObjectByID(playerid) if pl != nil { g.pm.Lock() g.player = pl g.pm.Unlock() } else { log.Printf("Game: HandlePacket: can't find our player %s", playerid) // just try again // XXX: find a better way time.AfterFunc(50*time.Millisecond, func() { g.ServerWChan <- gnet.NewPacket("Tgetplayer", nil) }) } // Rloadmap: get the map data from the server case "Rloadmap": gmap := pk.Data.(*game.MapChunk) g.Map = gmap default: log.Printf("bad packet tag %s", pk.Tag) } }
func (g *Game) Start() { log.Print("Game: Starting") // network setup server, err := g.config.Get("server", reflect.String) if err != nil { log.Fatal("Game: Start: missing server in config: %s", err) } con, err := net.Dial("tcp", server.(string)) if err != nil { log.Fatalf("Game: Start: Dial: %s", err) } g.ServerCon = con g.ServerRChan = chanio.NewReader(g.ServerCon) g.ServerWChan = chanio.NewWriter(g.ServerCon) if g.ServerRChan == nil || g.ServerWChan == nil { log.Fatal("Game: Start: can't establish channels") } // login username, err := g.config.Get("username", reflect.String) if err != nil { log.Fatal("Game: Start: missing username in config: %s", err) } g.ServerWChan <- gnet.NewPacket("Tconnect", username) // request the map from server g.ServerWChan <- gnet.NewPacket("Tloadmap", nil) // request the object we control // XXX: the delay is to fix a bug regarding ordering of packets. // if the client gets the response to this before he is notified // that the object exists, it will barf, so we delay this request. time.AfterFunc(50*time.Millisecond, func() { g.ServerWChan <- gnet.NewPacket("Tgetplayer", nil) }) // anonymous function that reads packets from the server go func(r <-chan interface{}) { for x := range r { p, ok := x.(*gnet.Packet) if !ok { log.Printf("Game: Read: Bogus server packet %#v", x) continue } g.HandlePacket(p) } log.Println("Game: Read: Disconnected from server!") io.WriteString(g.logpanel, "Disconnected from server!") }(g.ServerRChan) // terminal/keyhandling setup g.Terminal.Start() // chat dialog //g.TermLog = NewTermLog(image.Pt(g.Terminal.Rect.Width-VIEW_START_X-VIEW_PAD_X, 5)) // ESC to quit g.HandleKey(termbox.KeyEsc, func(ev termbox.Event) { g.CloseChan <- false }) // Enter to chat g.HandleKey(termbox.KeyEnter, func(ev termbox.Event) { g.SetInputHandler(g.chatpanel) }) // convert to func SetupDirections() for k, v := range CARDINALS { func(c rune, d game.Action) { g.HandleRune(c, func(_ termbox.Event) { // lol collision p := &gnet.Packet{"Taction", CARDINALS[c]} g.SendPacket(p) offset := game.DirTable[d] g.pm.Lock() defer g.pm.Unlock() oldposx, oldposy := g.player.GetPos() newpos := image.Pt(oldposx+offset.X, oldposy+offset.Y) if g.Map.CheckCollision(nil, newpos) { g.player.SetPos(newpos.X, newpos.Y) } }) /* scale := PLAYER_RUN_SPEED upperc := unicode.ToUpper(c) g.HandleRune(upperc, func(_ termbox.Event) { for i := 0; i < scale; i++ { g.Player.Move(d) } }) */ }(k, v) } }
// Handle Directionals func (gs *GameServer) HandleMovementPacket(cp *ClientPacket) { action := cp.Data.(game.Action) p := cp.Client.Player offset := game.DirTable[action] oldposx, oldposy := p.GetPos() newpos := image.Pt(oldposx+offset.X, oldposy+offset.Y) valid := true // check terrain collision if !gs.Map.CheckCollision(nil, newpos) { valid = false cp.Reply(gnet.NewPacket("Rchat", "Ouch! You bump into a wall.")) } // check gameobject collision for o := range gs.Objects.Chan() { // check if collision with Item and item name is flag px, py := o.GetPos() if px == newpos.X && py == newpos.Y { collfn := luar.NewLuaObjectFromName(gs.Lua, "collide") res, err := collfn.Call(p, o) if err != nil { log.Printf("GameServer: HandleMovementPacket: Lua error: %s", err) return } // only update position if collide returns true if thebool, ok := res.(bool); !ok || !thebool { log.Printf("GameServer: HandleMovementPacket: Lua collision failed") valid = false } else { // tell everyone that the colliders changed gs.SendPacketAll(gnet.NewPacket("Raction", o)) } if o.GetTag("player") { cp.Reply(gnet.NewPacket("Rchat", fmt.Sprintf("Ouch! You bump into %s.", o.GetName()))) // check if other player's got the goods for sub := range o.GetSubObjects().Chan() { if sub.GetTag("item") == true { // swap pop'n'lock // remove item from player swap := o.RemoveSubObject(sub) p.AddSubObject(swap) cp.Reply(gnet.NewPacket("Rchat", fmt.Sprintf("You steal a %s!", swap.GetName()))) } } } if o.GetTag("item") && o.GetTag("gettable") && valid { cp.Reply(gnet.NewPacket("Rchat", fmt.Sprintf("You see a %s here.", o.GetName()))) } } } if valid { cp.Client.Player.SetPos(newpos.X, newpos.Y) //gs.SendPacketAll(gnet.NewPacket("Raction", p)) } }
func (gs *GameServer) HandlePacket(cp *ClientPacket) { switch cp.Tag { // Tchat: chat message from a client case "Tchat": // broadcast chat chatline := cp.Data.(string) gs.SendPacketAll(gnet.NewPacket("Rchat", fmt.Sprintf("[chat] %s: %s", cp.Client.Username, chatline))) // Taction: movement request case "Taction": gs.HandleActionPacket(cp) // Tconnect: user establishes new connection case "Tconnect": username, ok := cp.Data.(string) if !ok { cp.Reply(gnet.NewPacket("Rerror", "invalid username or conversion failed")) break } else { cp.Client.Username = username } // make new player for client var newplayer game.Object newplayer = game.NewGameObject(username) newplayer.SetTag("player", true) newplayer.SetTag("visible", true) // setting this lets players pick up other players, lol //newplayer.SetTag("gettable", true) newplayer.SetGlyph(game.GLYPH_HUMAN) newplayer.SetPos(256/2, 256/2) // set the session's object cp.Client.Player = newplayer // put player object in world gs.Objects.Add(newplayer) // tell client about all other objects for o := range gs.Objects.Chan() { if o.GetID() != newplayer.GetID() { cp.Reply(gnet.NewPacket("Rnewobject", o)) } } // tell all clients about the new player gs.SendPacketAll(gnet.NewPacket("Rnewobject", newplayer)) // greet our new player cp.Reply(gnet.NewPacket("Rchat", "Welcome to Goland!")) case "Tdisconnect": // notify clients this player went away Action_ItemDrop(gs, cp) gs.Objects.RemoveObject(cp.Client.Player) gs.Detach(cp.Client) gs.SendPacketAll(gnet.NewPacket("Rdelobject", cp.Client.Player)) case "Tgetplayer": if cp.Client.Player != nil { cp.Reply(gnet.NewPacket("Rgetplayer", cp.Client.Player.GetID())) } else { cp.Reply(gnet.NewPacket("Rerror", "nil Player in WorldSession")) } case "Tloadmap": cp.Reply(gnet.NewPacket("Rloadmap", gs.Map)) default: log.Printf("GameServer: HandlePacket: unknown packet type %s", cp.Tag) } }
func (gs *GameServer) SendPkStrAll(tag string, data interface{}) { gs.SendPacketAll(gnet.NewPacket(tag, data)) }