func (m *Material) Load(version uint, data interface{}, attached []world.ObjectLike) { switch version { case 0: materials, _ := data.(map[string]interface{}) if materials == nil { materials = make(map[string]interface{}, 1) data = materials } if wood, ok := materials["w"].(uint64); ok { vol, ok := materials["vw"].(uint64) if !ok { vol = 100 } attached = append(attached, world.InitObject(&material{ wood: (*WoodType)(&wood), volume: vol, })) } if stone, ok := materials["s"].(uint64); ok { vol, ok := materials["vs"].(uint64) if !ok { vol = 100 } attached = append(attached, world.InitObject(&material{ stone: (*StoneType)(&stone), volume: vol, })) } if metal, ok := materials["m"].(uint64); ok { vol, ok := materials["vm"].(uint64) if !ok { vol = 100 } attached = append(attached, world.InitObject(&material{ metal: (*MetalType)(&metal), volume: vol, })) } if _, ok := materials["q"].(*big.Int); ok { // do nothing } else if quality, ok := materials["q"].(uint64); ok { materials["q"] = big.NewInt(int64(quality)) } else { materials["q"] = big.NewInt(1 << 62) } fallthrough case 1: dataMap := data.(map[string]interface{}) m.quality = *dataMap["q"].(*big.Int) m.components = make([]*material, len(attached)) for i, c := range attached { m.components[i] = c.(*material) } default: panic(fmt.Sprintf("version %d unknown", version)) } m.sortComponents() }
func (p *Player) Load(version uint, data interface{}, attached []world.ObjectLike) { switch version { case 0: dataMap := data.(map[string]interface{}) attached = append(attached, world.InitObject(&PlayerInstances{})) var err error dataMap["regt"], err = time.Parse(time.RFC3339Nano, dataMap["regt"].(string)) if err != nil { panic(err) } dataMap["lastt"], err = time.Parse(time.RFC3339Nano, dataMap["lastt"].(string)) if err != nil { panic(err) } fallthrough case 1: dataMap := data.(map[string]interface{}) p.Hero = *attached[0].(*Hero) world.InitObject(p) p.mtx.Lock() defer p.mtx.Unlock() p.ancestry = *attached[1].(*PlayerAncestry) p.instances = *attached[2].(*PlayerInstances) p.characterCreation = dataMap["creation"].(bool) p.zoneX, p.zoneY = dataMap["zx"].(int64), dataMap["zy"].(int64) p.tileX, p.tileY = dataMap["tx"].(uint8), dataMap["ty"].(uint8) p.zoneZ, _ = dataMap["zz"].(int8) p.login = dataMap["u"].(string) p.password = dataMap["p"].([]byte) p.admin, _ = dataMap["admin"].(bool) p.registered = dataMap["regt"].(time.Time) p.firstAddr = dataMap["rega"].(string) p.lastLogin = dataMap["lastt"].(time.Time) p.lastAddr = dataMap["lasta"].(string) default: panic(fmt.Sprintf("version %d unknown", version)) } for _, e := range p.equipped { if e.AdminOnly() && !p.admin { delete(p.equipped, e.slot) } else { e.wearer = p } } if !p.admin { for i := 0; i < len(p.Hero.items); i++ { if item, ok := p.Hero.items[i].(world.Item); !ok || item.AdminOnly() { p.Hero.items = append(p.Hero.items[:i], p.Hero.items[i+1:]...) i-- } } } }
func (r *Rock) Interact(player world.PlayerLike, action string) { switch action { default: r.Node.Interact(player, action) case "quarry": pos := r.Position() if pos == nil { return } x, y := pos.Position() player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, 0), &GatherSchedule{ Tool: player, Target_: r, Item: func(volume uint64) world.Visible { return world.InitObject(&Stone{ material: r.material.CopyStone(volume), }).(world.Visible) }, }, }, }) case "mine": if r.material.MetalColor() == "" { return } pos := r.Position() if pos == nil { return } x, y := pos.Position() player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, 0), &GatherSchedule{ Tool: player, Target_: r, Item: func(volume uint64) world.Visible { return world.InitObject(&Ore{ material: r.material.CopyMetal(volume), }).(world.Visible) }, }, }, }) } }
func (t *Tree) Interact(player world.PlayerLike, action string) { switch action { default: t.Node.Interact(player, action) case "chop": pos := t.Position() if pos == nil { return } x, y := pos.Position() player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, true, 0), &GatherSchedule{ Tool: player, Target_: t, Item: func(volume uint64) world.Visible { return world.InitObject(&Logs{ material: t.material.CopyWood(volume), }).(world.Visible) }, }, }, }) } }
func (e *Equip) Load(version uint, data interface{}, attached []world.ObjectLike) { switch version { case 0: material := &material.Material{} world.InitObject(material) material.Load(0, data.(map[string]interface{})["m"], nil) attached = []world.ObjectLike{material} fallthrough case 1: data.(map[string]interface{})["m"] = uint(1) fallthrough case 2: dataMap := data.(map[string]interface{}) e.slot = EquipSlot(dataMap["s"].(uint16)) e.kind = dataMap["k"].(uint64) colors := dataMap["c"].([]interface{}) if len(colors) > 0 { e.customColors = make([]string, len(colors)) for i, c := range colors { e.customColors[i] = c.(string) } } e.materials = make([]*material.Material, dataMap["m"].(uint)) for i := range e.materials { e.materials[i] = attached[i].(*material.Material) } attached = attached[len(e.materials):] default: panic(fmt.Sprintf("version %d unknown", version)) } }
func GenerateHeroOccupation(r *rand.Rand, race Race, occupation Occupation) *Hero { hero := &Hero{} world.InitObject(hero) hero.mtx.Lock() defer hero.mtx.Unlock() hero.birth = time.Now().UTC() hero.race = race hero.occupation = occupation hero.gender = race.Genders()[r.Intn(len(race.Genders()))] hero.skinTone = uint(r.Intn(len(race.SkinTones()))) switch race { case RaceHuman: hero.name = GenerateHumanName(r, hero.gender) } hero.equip(&Equip{ slot: SlotShirt, kind: 0, customColors: []string{randomColor(r)}, }) hero.equip(&Equip{ slot: SlotPants, kind: 0, customColors: []string{randomColor(r)}, }) hero.equip(&Equip{ slot: SlotFeet, kind: 0, }) return hero }
func (m *Material) copy(volume uint64, filter func(*material) bool) *Material { copy := &Material{ quality: *(&big.Int{}).Set(&m.quality), } world.InitObject(copy) var total uint64 for _, c := range m.components { if filter(c) { total += c.volume copyc := *c copy.components = append(copy.components, ©c) } } if total == volume { return copy } for _, c := range copy.components { v := c.volume * volume / total total -= c.volume volume -= v c.volume = v } return copy }
func (l *Logs) Load(version uint, data interface{}, attached []world.ObjectLike) { switch version { case 0: material := &Material{components: []*material{&material{}}} world.InitObject(material) world.InitObject(material.components[0]) kind := WoodType(data.(uint64)) material.components[0].wood = &kind material.components[0].volume = 100 material.quality = *big.NewInt(1 << 62) attached = append(attached, material) fallthrough case 1: l.material = attached[0].(*Material) default: panic(fmt.Sprintf("version %d unknown", version)) } }
func (f *Floor) Load(version uint, data interface{}, attached []world.ObjectLike) { switch version { case 0: // no fields in version 0 case 1: f.material = &Material{} world.InitObject(f.material) f.material.Load(0, data.(map[string]interface{}), nil) case 2: f.material = attached[0].(*Material) default: panic(fmt.Sprintf("version %d unknown", version)) } }
func WrapSpawnFunc(f func(*Material, string) world.Visible) func(string) world.Visible { return func(s string) world.Visible { prefix := func(p string) (bool, uint64) { if len(s) > len(p) && s[:len(p)] == p { if s[len(p)] == ' ' { s = s[len(p)+1:] return true, 100 } if s[len(p)] == ':' { l := len(p) + 1 for ; l < len(s)-2 && s[l] != ' '; l++ { } if s[l] == ' ' { volume, err := strconv.ParseUint(s[len(p)+1:l], 0, 64) if err == nil { s = s[l+1:] return true, volume } } } } return false, 0 } var m Material world.InitObject(&m) m.quality.SetUint64(1000) var setQuality bool find: for { m.sortComponents() if v := f(&m, s); v != nil { return v } if !setQuality && len(s) > 3 && s[0] == 'q' { var l int for l = 0; l < len(s)-2 && s[l] != ' '; l++ { } if s[l] == ' ' { var quality big.Int _, ok := quality.SetString(s[1:l], 10) if ok { m.quality = quality setQuality = true s = s[l+1:] continue } } } for i := range woodTypes { t := WoodType(i) if ok, volume := prefix(t.Data().Name()); ok { m.components = append(m.components, &material{ wood: &t, volume: volume, }) continue find } } for i := range stoneTypes { t := StoneType(i) if ok, volume := prefix(t.Data().Name()); ok { m.components = append(m.components, &material{ stone: &t, volume: volume, }) continue find } } for i := range metalTypes { t := MetalType(i) if ok, volume := prefix(t.Data().Name()); ok { m.components = append(m.components, &material{ metal: &t, volume: volume, }) continue find } } return nil } } }
func (f *Forge) Interact(player world.PlayerLike, action string) { switch action { default: f.VisibleObject.Interact(player, action) case "smelt": pos := f.Position() ppos := player.Position() if pos == nil || ppos == nil { return } x, y := pos.Position() if px, py := ppos.Position(); px != x || py != y+1 { player.SetSchedule(&world.ScheduleSchedule{ Schedules: []world.Schedule{ world.NewWalkSchedule(x, y, false, uint(player.Weight()/player.WeightMax())), &world.ActionSchedule{ Target_: f, Action: action, }, }, }) return } ores := []interface{}{} for _, v := range player.Inventory() { if o, ok := v.(*Ore); ok { ores = append(ores, map[string]interface{}{ "i": o.NetworkID(), "q": o.Material().Quality(), "v": o.Volume(), "w": o.Weight(), }) } } for _, v := range player.Inventory() { if s, ok := v.(*Stone); ok { ores = append(ores, map[string]interface{}{ "i": s.NetworkID(), "q": s.Material().Quality(), "v": s.Volume(), "w": s.Weight(), }) } } instance := player.Instance(f.Position()) var done time.Time instance.Last(func(last time.Time) time.Time { done = last return last }) contents := []interface{}{} instance.Items(func(items []world.Visible) []world.Visible { for _, i := range items { var sprites []map[string]interface{} for j, c := range i.Colors() { sprites = append(sprites, map[string]interface{}{ "S": i.Sprite(), "C": c, "E": map[string]interface{}{ "y": j, }, }) } contents = append(contents, sprites) // 4 minutes per 5kg of ore done = done.Add(time.Minute * 4 * time.Duration(i.(world.Item).Weight()) / 5000) } if done.Before(time.Now()) && len(items) > 0 { materials := make(map[MetalType]*material) var quality big.Int var totalVolume uint64 var tmp big.Int for _, i := range items { if o, ok := i.(*Ore); ok { for _, m := range o.material.components { if m.metal == nil || m.volume == 0 { continue } mat := materials[*m.metal] if mat == nil { mat = &material{metal: m.metal} materials[*m.metal] = mat } totalVolume += m.volume mat.volume += m.volume tmp.SetUint64(m.volume) quality.Add(&quality, tmp.Mul(&o.material.quality, &tmp)) } } } originalVolume := totalVolume quality.Div(&quality, tmp.SetUint64(totalVolume)) makeIngot := func(volume uint64) { ingot := &Ingot{} world.InitObject(ingot) ingot.material = &Material{ components: make([]*material, 0, len((materials))), quality: quality, } world.InitObject(ingot.material) remaining := originalVolume for _, m := range materials { v := m.volume * volume / remaining remaining -= m.volume volume -= v ingot.material.components = append(ingot.material.components, &material{ metal: m.metal, volume: v, }) } ingot.material.sortComponents() if !player.GiveItem(ingot) { player.Position().Add(ingot) } } for totalVolume != 0 { if totalVolume <= 1000 { makeIngot(totalVolume) break } makeIngot(1000) totalVolume -= 1000 } items = items[:0] contents = contents[:0] } return items }) player.SetHUD("forge", map[string]interface{}{ "O": ores, "C": contents, "T": done, }) } }
func socketHandler(ws *websocket.Conn) { defer ws.Close() addr, _, err := net.SplitHostPort(ws.Request().RemoteAddr) if err != nil { panic(err) } packets := make(chan clientPacket) go func() { defer close(packets) for { var packet clientPacket if err := websocket.JSON.Receive(ws, &packet); err != nil { if err == io.EOF { return } if _, ok := err.(*net.OpError); ok { return } log.Printf("%s: %T %v", addr, err, err) return } packets <- packet } }() err = websocket.JSON.Send(ws, packetClientHash) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetClientHash, err) } return } var ( player *hero.Player zone *world.Zone kick <-chan string hud <-chan hero.HUD inventory <-chan []world.Visible messages <-chan []hero.Message updateQueue packetUpdate ) updateTick := time.NewTicker(time.Second / 10) defer updateTick.Stop() updates := make(chan []packetUpdateUpdate, 1) floatingText := make(chan packetFloatingText, 32) sendUpdates := func(newUpdates ...packetUpdateUpdate) { for { select { case updates <- newUpdates: return case other := <-updates: newUpdates = append(other, newUpdates...) } } } var listener *world.ZoneListener updateWalls := func(t *world.Tile, obj world.ObjectLike) { if _, ok := obj.(material.IsWall); ok { x, y := t.Position() if x > 0 { tile := t.Zone().Tile(x-1, y) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } if x < 255 { tile := t.Zone().Tile(x+1, y) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } if y > 0 { tile := t.Zone().Tile(x, y-1) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } if y < 255 { tile := t.Zone().Tile(x, y+1) for _, w := range tile.Objects() { if _, ok := w.(material.IsWall); ok { listener.Update(tile, w) } } } } } toObject := func(o world.Visible) *packetUpdateObject { var health *[2]uint64 if c, ok := o.(world.Combat); ok { h, m := c.Health(), c.MaxHealth() if l := m.BitLen(); l > 64 { h = (&big.Int{}).Rsh(h, uint(l-64)) m = (&big.Int{}).Rsh(m, uint(l-64)) } health = &[2]uint64{h.Uint64(), m.Uint64()} } return addSprites(&packetUpdateObject{ Name: o.Name(), Actions: o.Actions(player), Health: health, }, o) } listener = &world.ZoneListener{ Add: func(t *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: x, Y: y, Object: toObject(o), }) updateWalls(t, obj) }, Remove: func(t *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: x, Y: y, Remove: true, }) updateWalls(t, obj) }, Move: func(from, to *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } fx, fy := from.Position() tx, ty := to.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: tx, Y: ty, FromX: &fx, FromY: &fy, }) updateWalls(from, obj) updateWalls(to, obj) }, Update: func(t *world.Tile, obj world.ObjectLike) { o, ok := obj.(world.Visible) if !ok { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: o.NetworkID(), X: x, Y: y, Object: toObject(o), }) }, Damage: func(attacker, victim world.Combat, amount *big.Int) { t := victim.Position() if t == nil { return } x, y := t.Position() sendUpdates(packetUpdateUpdate{ ID: victim.NetworkID(), X: x, Y: y, Object: toObject(victim), }) text := packetFloatingTextText{ X: x, Y: y, Color: "#fff", } if amount == world.DamageMissed { text.Text = "MISS" } else if amount == world.DamageBlocked { text.Text = "BLOCK" } else if amount == world.DamageResisted { text.Text = "RESIST" } else { text.Text = material.Comma(amount) } select { case floatingText <- packetFloatingText{text}: default: } }, } next: for { select { case packet, ok := <-packets: if !ok { return } if packet.Auth != nil { if player != nil { return } var fail string player, fail = hero.Login(addr, packet.Auth) if fail != "" { err = websocket.JSON.Send(ws, packetKick{fail}) if err != nil && err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetKick{fail}, err) } return } world.InitObject(player) kick, hud, inventory, messages = player.InitPlayer() zx, tx, zy, ty, zz := player.LoginPosition() defer hero.PlayerDisconnected(player) zone = world.GetZone(zx, zy, zz) zone.AddListener(listener) if player.CanSpawn() { zone.Tile(tx, ty).Add(player) } else { player.CharacterCreation("") } defer func() { if t := player.Position(); t != nil { player.UpdatePosition() t.Remove(player) } zone.RemoveListener(listener) world.ReleaseZone(zone) }() inventoryObjects := []*packetInventoryItem{} for _, item := range player.Inventory() { inventoryObjects = append(inventoryObjects, &packetInventoryItem{ ID: item.NetworkID(), Object: addSprites(&packetUpdateObject{ Name: item.Name(), Actions: item.Actions(player), }, item), }) } err = websocket.JSON.Send(ws, packetInventory{inventoryObjects}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetInventory{inventoryObjects}, err) } return } } if player == nil { continue } if packet.Walk != nil { if t := player.Position(); t != nil { x, y := t.Position() if packet.Walk.X == x && packet.Walk.Y == y { player.ClearSchedule() } else { player.SetSchedule(world.NewWalkSchedule(packet.Walk.X, packet.Walk.Y, false, uint(player.Weight()/player.WeightMax()))) } } } if packet.HUD != nil { switch packet.HUD.Name { default: return case "cc": if cmd, ok := packet.HUD.Data.(string); ok { player.CharacterCreation(cmd) } else { return } case "forge": data, ok := packet.HUD.Data.(map[string]interface{}) if !ok { return } pos := player.Position() if pos == nil { return } x, y := pos.Position() if y == 0 { return } y-- t := pos.Zone().Tile(x, y) for _, o := range t.Objects() { if forge, ok := o.(*material.Forge); ok { forge.Command(player, data) continue next } } return } } if packet.Chat != nil { if strings.TrimSpace(*packet.Chat) != "" { player.Chat(addr, *packet.Chat) } } if packet.Mouse != nil { color := "#0f0" if player.Position() == nil || player.Position().Zone().Tile(packet.Mouse.X, packet.Mouse.Y).Blocked() { color = "#f00" } update := map[string][]map[string]interface{}{ "Update": { { "I": "_crosshair", "X": packet.Mouse.X, "Y": packet.Mouse.Y, "O": map[string][]packetUpdateSprite{ "S": { { Sheet: "ui_icons", Color: color, Extra: map[string]interface{}{ "x": 1, }, }, }, }, }, }, } if packet.Mouse.Fx != packet.Mouse.X || packet.Mouse.Fy != packet.Mouse.Y { update["Update"] = append([]map[string]interface{}{{ "I": "_crosshair", "R": true, "X": packet.Mouse.Fx, "Y": packet.Mouse.Fy, }}, update["Update"]...) } err = websocket.JSON.Send(ws, update) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, update, err) } return } } if packet.Interact != nil { if packet.Interact.X < 8 { inv := player.Inventory() i := int(packet.Interact.X) + 8*int(packet.Interact.Y) if len(inv) > i { if inv[i].NetworkID() == packet.Interact.ID { inv[i].Interact(player, packet.Interact.Action) continue } } } if player.Position() == nil { continue } for _, obj := range player.Position().Zone().Tile(packet.Interact.X, packet.Interact.Y).Objects() { if v, ok := obj.(world.Visible); ok && v.NetworkID() == packet.Interact.ID { v.Interact(player, packet.Interact.Action) break } } } if len(packet.Admin) > 0 { player.AdminCommand(addr, packet.Admin...) } case message := <-kick: websocket.JSON.Send(ws, packetKick{message}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetKick{message}, err) } return } return case h := <-hud: err = websocket.JSON.Send(ws, packetHUD{h}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetHUD{h}, err) } return } case items := <-inventory: objects := make([]*packetInventoryItem, len(items)) for i, item := range items { objects[i] = &packetInventoryItem{ ID: item.NetworkID(), Object: addSprites(&packetUpdateObject{ Name: item.Name(), Actions: item.Actions(player), }, item), } } err = websocket.JSON.Send(ws, packetInventory{objects}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetInventory{objects}, err) } return } case update := <-updates: updateQueue.Update = append(updateQueue.Update, update...) case text := <-floatingText: err = websocket.JSON.Send(ws, text) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, text, err) } return } case <-updateTick.C: if len(updateQueue.Update) == 0 { continue } if player == nil { continue } leftover := updateQueue.Update[:0] if len(updateQueue.Update) > 100 { updateQueue.Update, leftover = updateQueue.Update[:100], updateQueue.Update[100:] } if t := player.Position(); t != nil { updateQueue.PlayerX, updateQueue.PlayerY = t.Position() } else { updateQueue.PlayerX, updateQueue.PlayerY = 127, 127 } err = websocket.JSON.Send(ws, updateQueue) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, updateQueue, err) } return } updateQueue.Update = leftover case msg := <-messages: err = websocket.JSON.Send(ws, packetMessage{msg}) if err != nil { if err != io.EOF { log.Printf("[err_sock] %s:%#v %v", addr, packetMessage{msg}, err) } return } } } }
func (h *Hero) Load(version uint, data interface{}, attached []world.ObjectLike) { h.mtx.Lock() defer h.mtx.Unlock() switch version { case 0: dataMap := data.(map[string]interface{}) if dataMap["skin"] == nil { dataMap["skin"] = uint(rand.Intn(len(Race(dataMap["race"].(uint64)).SkinTones()))) } if dataMap["equipped"] == nil { r := rand.New(rand.NewSource(rand.Int63())) h.equip(&Equip{ slot: SlotShirt, kind: 0, customColors: []string{randomColor(r)}, }) h.equip(&Equip{ slot: SlotPants, kind: 0, customColors: []string{randomColor(r)}, }) h.equip(&Equip{ slot: SlotFeet, kind: 0, }) } var err error dataMap["birth"], err = time.Parse(time.RFC3339Nano, dataMap["birth"].(string)) if err != nil { panic(err) } dataMap["death"], err = time.Parse(time.RFC3339Nano, dataMap["death"].(string)) if err != nil { panic(err) } fallthrough case 1: dataMap := data.(map[string]interface{}) h.CombatObject = *attached[0].(*world.CombatObject) world.InitObject(h) h.name.unserialize(dataMap["name"].(map[string]interface{})) h.race = Race(dataMap["race"].(uint64)) h.gender = Gender(dataMap["gender"].(uint64)) h.occupation = Occupation(dataMap["occupation"].(uint64)) h.skinTone = dataMap["skin"].(uint) if h.equipped == nil { equipCount := dataMap["equipped"].(uint) for _, o := range attached[1 : equipCount+1] { h.equip(o.(*Equip)) } itemCount := dataMap["items"].(uint) h.items = make([]world.Visible, itemCount) for i, o := range attached[equipCount+1 : itemCount+equipCount+1] { h.items[i] = o.(world.Visible) } } default: panic(fmt.Sprintf("version %d unknown", version)) } }
// Login returns a non-nil player and an empty error string OR a nil player // and an error to show to the user. func Login(addr string, packet *LoginPacket) (*Player, string) { login := strings.TrimSpace(packet.Login) if login == "" { return nil, "A username is required." } pass := []byte(packet.Pass) if len(pass) <= 2 { return nil, "A password is required." } filename := loginToFilename(login) loginLock.Lock() defer loginLock.Unlock() loginAttempts[addr]++ if loginAttempts[addr] == 5 { loginAttempts[addr] += 60 } if loginAttempts[addr] > 5 { return nil, fmt.Sprintf("Too many login attempts. Come back in %d minutes.", loginAttempts[addr]-5) } f, err := os.Open(filename) if err != nil { hashedPass, err := bcrypt.GenerateFromPassword(pass, CryptoCost) if err != nil { panic(err) } p := &Player{ Hero: *GenerateHero(rand.New(rand.NewSource(rand.Int63()))), characterCreation: true, tileX: 127, tileY: 127, login: login, password: hashedPass, firstAddr: addr, registered: time.Now().UTC(), lastAddr: addr, lastLogin: time.Now().UTC(), } world.InitObject(p) for _, e := range p.Hero.equipped { e.wearer = p } savePlayer(p) onlinePlayers[login] = p return p, "" } defer f.Close() g, err := gzip.NewReader(f) if err != nil { panic(err) } defer g.Close() var data interface{} err = gob.NewDecoder(g).Decode(&data) if err != nil { panic(err) } p := world.LoadConvert(data).(*Player) if bcrypt.CompareHashAndPassword(p.password, pass) != nil { return nil, "Username or password incorrect." } loginAttempts[addr]-- if other, ok := onlinePlayers[p.login]; ok { savePlayer(other) other.Kick("Logged in from a different location.") _, err = f.Seek(0, 0) if err != nil { panic(err) } g, err = gzip.NewReader(f) if err != nil { panic(err) } defer g.Close() data = nil err = gob.NewDecoder(g).Decode(&data) if err != nil { panic(err) } p = world.LoadConvert(data).(*Player) } cost, err := bcrypt.Cost(p.password) if err != nil { panic(err) } if cost != CryptoCost { p.password, err = bcrypt.GenerateFromPassword(pass, CryptoCost) if err != nil { panic(err) } savePlayer(p) } onlinePlayers[p.login] = p return p, "" }