Пример #1
0
func (p *Player) CharacterCreation(command string) {
	switch command {
	default:
		return
	case "":
	case "gender":
		p.mtx.Lock()
		var index int
		for i, g := range p.race.Genders() {
			if g == p.gender {
				index = (i + 1) % len(p.race.Genders())
				break
			}
		}
		p.gender = p.race.Genders()[index]
		p.mtx.Unlock()
		fallthrough
	case "name":
		p.mtx.Lock()
		switch p.race {
		case RaceHuman:
			p.name = GenerateHumanName(rand.New(rand.NewSource(rand.Int63())), p.gender)
		}
		p.mtx.Unlock()
	case "skin":
		p.mtx.Lock()
		p.skinTone = (p.skinTone + 1) % uint(len(p.race.SkinTones()))
		p.mtx.Unlock()
	case "shirt":
		p.mtx.Lock()
		p.equipped[SlotShirt].customColors[0] = randomColor(rand.New(rand.NewSource(rand.Int63())))
		p.mtx.Unlock()
	case "pants":
		p.mtx.Lock()
		p.equipped[SlotPants].customColors[0] = randomColor(rand.New(rand.NewSource(rand.Int63())))
		p.mtx.Unlock()
	case "accept":
		p.mtx.Lock()
		z := world.GetZone(p.zoneX, p.zoneY, p.zoneZ)
		p.characterCreation = false
		tile := z.Tile(p.tileX, p.tileY)
		p.mtx.Unlock()
		tile.Add(p)
		world.ReleaseZone(z)
		p.ClearHUD()
		return
	}

	p.mtx.Lock()
	defer p.mtx.Unlock()

	var sprites []map[string]interface{}

	w, h := p.race.SpriteSize()
	sprites = append(sprites, map[string]interface{}{
		"C": p.race.SkinTones()[p.skinTone],
		"S": p.race.Sprite(),
		"E": map[string]interface{}{
			"a": "ccr",
			"s": 4,
			"w": w,
			"h": h,
		},
	})

	for _, e := range p.equipped {
		w, h := e.SpriteSize()
		for _, c := range e.Colors() {
			sprites = append(sprites, map[string]interface{}{
				"C": c,
				"S": e.Sprite(),
				"E": map[string]interface{}{
					"a": "ccr",
					"s": 4,
					"w": w,
					"h": h,
				},
			})
		}
	}

	p.SetHUD("cc", map[string]interface{}{
		"S": sprites,
		"N": p.name.Name(),
		"G": p.gender.Name(),
	})
}
Пример #2
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
			}
		}
	}
}