Beispiel #1
0
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()
}
Beispiel #2
0
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--
			}
		}
	}
}
Beispiel #3
0
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)
					},
				},
			},
		})
	}
}
Beispiel #4
0
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)
					},
				},
			},
		})
	}
}
Beispiel #5
0
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))
	}
}
Beispiel #6
0
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
}
Beispiel #7
0
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, &copyc)
		}
	}

	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
}
Beispiel #8
0
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))
	}
}
Beispiel #9
0
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))
	}
}
Beispiel #10
0
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
		}
	}
}
Beispiel #11
0
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,
		})
	}
}
Beispiel #12
0
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
			}
		}
	}
}
Beispiel #13
0
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))
	}
}
Beispiel #14
0
// 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, ""
}