IT评测·应用市场-qidao123.com技术社区

标题: 【精简改造版】大型多人在线游戏BrowserQuest服务器Golang框架解析(2)— [打印本页]

作者: 千千梦丶琪    时间: 2024-7-23 18:17
标题: 【精简改造版】大型多人在线游戏BrowserQuest服务器Golang框架解析(2)—
1.架构选型

B/S架构:支持PC、平板、手机等多个平台
2.技术选型

(1)客户端web技术:

(2)背景

(3)通讯范例:websocket
(4)通讯协议:[type(int), ……]
3.服务架构范例

单体架构
4.数据结构

4.1 实体范例

实体分类
编号
范例
说明
Player
1
WARRIOR
战士
Mobs
2
RAT
老鼠
3
SKELETON
骷髅
4
GOBLIN
妖精(哥布林)
5
OGRE
食人魔
6
SPECTRE
幽灵、妖怪
7
CRAB
螃蟹
8
BAT
蝙蝠
9
WIZARD
巫师
10
EYE

11
SNAKE

12
SKELETON2
骷髅2
13
BOSS
14
DEATHKNIGHT
死亡骑士
防具(Armors)
20
FIREFOX
火狐
21
CLOTHARMOR
布衣
22
LEATHERARMOR
皮衣
23
MAILARMOR
铠甲
24
PLATEARMOR
鳞甲
25
REDARMOR
红衣
26
GOLDENARMOR
金色战甲
Objects
35
FLASK
烧瓶
36
BURGER
汉堡
37
CHEST
箱子
38
FIREPOTION
魔药
39
CAKE
蛋糕
NPCs
40
GUARD
卫兵
41
KING
国王
42
OCTOCAT
章鱼猫
43
VILLAGEGIRL
村民(女)
44
VILLAGER
村民(男)
45
PRIEST
牧师
46
SCIENTIST
科学家
47
AGENT
特工
48
RICK
干草堆
49
NYAN
50
SORCERER
男巫师
51
BEACHNPC
海滨NPC
52
FORESTNPC
丛林NPC
53
DESERTNPC
戈壁NPC
54
LAVANPC
火山NPC
55
CODER
程序员
Weapons
60
SWORD1
剑1
61
SWORD2
剑2
62
REDSWORD
红剑
63
GOLDENSWORD
金剑
64
MORNINGSTAR
晨星
65
AXE
斧子
66
BLUESWORD
蓝剑
4.2 地图定义

字段
范例
初始值
范围
说明
width
int
172
地图宽
height
int
314
地图高
collisions
list[int]
碰撞点
doors
list[object]

doors.[].x
int
门x坐标
doors.[].y
int
门y坐标
doors.[].p
int
0/1
doors.[].tcx
int
doors.[].tcy
int
doors.[].to
string
u/d/l/r
门朝向
doors.[].tx
int
目标x
doors.[].ty
int
目标y
checkpoints
list[object]
checkpoints.[].id
int
checkpoints.[].x
int
checkpoints.[].y
int
checkpoints.[].w
int
checkpoints.[].h
int
checkpoints.[].s
int
0/1
roamingAreas
list[object]
移动地区
roamingAreas.[].id
int
roamingAreas.[].x
int
roamingAreas.[].y
int
roamingAreas.[].width
int
roamingAreas.[].height
int
roamingAreas.[].type
string
rat、crab、goblin……
怪物范例
roamingAreas.[].nb
int
数量
chestAreas
list[object]
箱子地区
chestAreas.[].x
int
chestAreas.[].y
int
chestAreas.[].w
int
chestAreas.[].h
int
chestAreas.[].i
list[int]
箱子中ItemList
chestAreas.[].tx
int
chestAreas.[].ty
int
staticChests
list[object]
静态箱子
staticChests.[].x
int
staticChests.[].y
int
staticChests.[].i
list[int]
箱子中ItemList
staticEntities
object
静态实体
staticEntities.key
int-string
staticEntities.value
string
rat、crab、goblin……
tilesize
int
16
瓦片大小
5.通讯协议

5.1 消息范例定义

客户端与服务器基于websocket毗连举行数据收发,具体协议如下:
通讯范例
编号
消息范例
参数
寄义
备注
服务端-->客户端
1
WELCOME
id,name,x,y,hp
接待信息
4
MOVE
id,x,y
移动信息
双向消息
5
LOOTMOVE
id,item
朝向ITEM移动捡取
双向消息
7
ATTACK
attacker,target
攻击信息
双向消息
2
SPAWN
id,kind,x,y
再生信息
3
DESPAWN
id
取消再生
SPAWN_BATCH
批量再生
10
HEALTH
points,[isRegen]
康健信息
11
CHAT
id,text
谈天信息
双向消息
13
EQUIP
id,itemKind
装备信息
14
DROP
mobId,id,kind,playersInvolved
掉落信息
15
TELEPORT
id,x,y
传送信息
16
DAMAGE
id,dmg
伤害信息
17
POPULATION
worldPlayers,totalPlayers
生齿数量信息
19
LIST
列表信息
22
DESTROY
id
销毁信息
18
KILL
mobKind
杀死信息
23
HP
maxHP
生命信息
24
BLINK
id
闪烁
客户端-->服务端
0
HELLO
player.name,
招呼
4
MOVE
x,y
移动
双向消息
5
LOOTMOVE
x,y,item.id
移动捡取
双向消息
6
AGGRO
mob.id
7
ATTACK
mob.id
攻击
双向消息
8
HIT
mob.id
开始攻击
9
HURT
mob.id
伤害
11
CHAT
text
谈天
双向消息
12
LOOT
item.id
捡取
15
TELEPORT
x,y
传送
双向消息
20
WHO
ids
信息查询
21
ZONE
-
地区切换
玩家从一个地区走到另外地区
25
OPEN
chest.id
打开箱子
26
CHECK
id
确认
5.2 协议交互流程


6.类图



7.线程模子


7.1 协程创建


7.2 协程通讯

(1)Handler协程与PlayerHandleLoop协程通过带缓冲PacketChan通讯
(2)Player读取解析PacketChan中的消息,逻辑处置惩罚后投递到所属地区对象的zone.EventCh
(3)Player对象调用世界对象,将消息投递到world.BroadcastCh举行世界消息发送(如人数)
(4)世界对象解析world.BroadcastCh中的消息,遍历所有地区对象,将消息投递到zone.EventCh
(5)地区对象读取解析zone.EventCh中的消息,逻辑处置惩罚后调用Player对象send方法举行消息发送
8.游戏具体处置惩罚逻辑分析

8.1地图加载

(1)通过json Unmarshal举行decode到Map结构体。
(2)根据地图宽高和地区宽高,计算出地区个数
(3)此中Map.collitions表示碰撞的点,团结地图宽高,初始化碰撞二维表
(4)初始化checkpoint Map,checkpoint ID作为KEY。此中checkpoint.S为1的表示为起始地区
8.2.物品掉落

  1.         TypeCrab.ID: &MobProperty{
  2.                 Drops: map[string]int{
  3.                         "flask":        50,
  4.                         "axe":          20,
  5.                         "leatherarmor": 10,
  6.                         "firepotion":   5,
  7.                 },
  8.                 HP:          60,
  9.                 ArmorLevel:  2,
  10.                 WeaponLevel: 1,
  11.         },
复制代码
Drops表示:flask:50%,axe:20%,leatherarmor:%10,firepotion:5%,不掉落5%
算法:随机一个[0~99]的值,累计求和,判定是否在Drops区间,如果在则掉落对应物品,否则不掉落。
8.3.物品捡取

  1. func (z *Zone) onLoot(e *Event) {
  2.         itemID := e.Data[0].(int)
  3.         p := z.PlayersMap[e.PlayerID]
  4.         if p == nil {
  5.                 return
  6.         }
  7.         if item := z.ItemsMap[itemID]; item != nil {
  8.                 despawnEvent := AquireEvent(EventDespawn, itemID)
  9.                 z.broadcastZone(despawnEvent)
  10.                 item.IsDestroy = true
  11.                 if item.IsStatic {
  12.                         item.RespawnLater(z.EventCh)
  13.                 }
  14.                 kind := item.Kind
  15.                 if kind.ID == TypeFirePotion.ID {
  16.                         // TODO
  17.                 } else if IsHealingItem(kind) {
  18.                         amount := 0
  19.                         switch kind.ID {
  20.                         case TypeFlask.ID:
  21.                                 amount = 40
  22.                         case TypeBurger.ID:
  23.                                 amount = 100
  24.                         }
  25.                         if amount > 0 && !p.HasFullHealth() {
  26.                                 p.ReginHealthBy(amount)
  27.                                 healthEvent := AquireEvent(EventHealth, p.HP)
  28.                                 _ = p.send(healthEvent)
  29.                         }
  30.                 } else if IsArmor(kind) || IsWeapon(kind) {
  31.                         equipEvent := AquireEvent(EventEquip, p.ID, kind.ID)
  32.                         z.broadcastZone(equipEvent)
  33.                         if IsArmor(kind) {
  34.                                 p.equipArmor(kind.ID)
  35.                                 p.updateHP()
  36.                                 HPEvent := AquireEvent(EventHP, p.MaxHP)
  37.                                 _ = p.send(HPEvent)
  38.                         } else {
  39.                                 p.equipWeapon(kind.ID)
  40.                         }
  41.                 }
  42.         }
  43. }
复制代码
捡取流程:
通过EventDespawn消息广播消散;

8.4.mob跟随

  1. func (m *Mob) ChaseTarget(zoneID string, mp *Map, targetX, targetY int) {
  2.         zid := mp.GetGroupIDFromPosition(targetX, targetY)
  3.         if zoneID != zid {
  4.                 m.X, m.Y = targetX, targetY
  5.         } else {
  6.                 pointsAround := make([][2]int, 0)
  7.                 for _, p := range [][2]int{
  8.                         [2]int{targetX, targetY + 1},
  9.                         [2]int{targetX + 1, targetY},
  10.                         [2]int{targetX, targetY - 1},
  11.                         [2]int{targetX - 1, targetY},
  12.                 } { // 沿着玩家上下左右,找到若干个有效的点作为目标
  13.                         if mp.IsValidPosition(p[0], p[1]) && zoneID == mp.GetGroupIDFromPosition(p[0], p[1]) {
  14.                                 pointsAround = append(pointsAround, p)
  15.                         }
  16.                 }
  17.                 minLen := 999999
  18.                 minIndex := 0
  19.                 for i, p := range pointsAround { // 基于有效点,找到其中mob到玩家有效点的一个最小距离
  20.                         pathLength := (m.X-p[0])*(m.X-p[0]) + (m.Y-p[1])*(m.Y-p[1])
  21.                         if pathLength <= minLen {
  22.                                 minLen = pathLength
  23.                                 minIndex = i
  24.                         }
  25.                 }
  26.                 m.X, m.Y = pointsAround[minIndex][0], pointsAround[minIndex][1]
  27.         }
  28. }
复制代码
算法:先找玩家周围有效点,然后从中计算选取一个最短路径点,最短路径通过:(x1-x2)(x1-x2) + (y1-y2)(y1-y2)粗略算出。更新当前mob的X、Y。
8.5.mob平静期处置惩罚

  1. func (z *Zone) onMobCalm(e *Event) {
  2.         mobID := e.Data[0].(int)
  3.         if mob := z.MobsMap[mobID]; mob != nil {
  4.                 z.Logger.Println("[DEBUG] Mob", mob, "Calm Down")
  5.                 mob.RecoveryHP()
  6.                 for k := range mob.Haters {
  7.                         delete(mob.Haters, k)
  8.                 }
  9.                 mob.TargetID = 0
  10.                 if mob.X != mob.OriginX || mob.Y != mob.OriginY {
  11.                         mob.X, mob.Y = mob.OriginX, mob.OriginY
  12.                         moveEvent := AquireEvent(EventMove, mob.ID, mob.X, mob.Y)
  13.                         z.broadcastZone(moveEvent)
  14.                 }
  15.                 mob.TargetID = 0
  16.         }
  17. }
复制代码
平静期到时(如果有玩家HIT攻击此mob时,平静期会被重置),mob恢复体力,扫除所有Haters,当前位置不在原始位置则移动到原始位置并广播。
8.6.多人同时攻击

  1. func (m *Mob) AddHate(playerID, damage int) {
  2.         m.Haters[playerID] += damage
  3. }
  4. func (m *Mob) ChooseMobTarget() int {
  5.         var max, maxPid int
  6.         for pid, hate := range m.Haters {
  7.                 if hate > max {
  8.                         max = hate
  9.                         maxPid = pid
  10.                 }
  11.         }
  12.         if max <= 0 {
  13.                 return -1
  14.         }
  15.         return maxPid
  16. }
  17. func (z *Zone) onMobAttacked(m *Mob, p *Player) {
  18.         m.ResetHateLater(z.EventCh)
  19.         dmg := DamageFormula(p.WeaponLevel, m.ArmorLevel)
  20.         if dmg > 0 {
  21.                 m.HP -= dmg
  22.                 if m.HP > 0 {
  23.                         dmgEvent := AquireEvent(EventDamage, m.ID, dmg)
  24.                         _ = p.send(dmgEvent)
  25.                         m.AddHate(p.ID, dmg)
  26.                         if maxHateTarget := m.ChooseMobTarget(); maxHateTarget > 0 {
  27.                                 if maxHateTarget != m.TargetID {
  28.                                         m.TargetID = maxHateTarget
  29.                                 }
  30.                                 attackEvent := AquireEvent(EventAttack, m.ID, m.TargetID)
  31.                                 z.broadcastZone(attackEvent)
  32.                         }
  33.                 } else {
  34.                         z.Logger.Println("[DEBUG] m", m.ID, "DEAD!")
  35.                         m.IsDead = true
  36.                         if dropItem := m.DropItem(); dropItem != nil {
  37.                                 z.Logger.Println("[DEBUG] m", m.ID, "DROP!", dropItem)
  38.                                 dropItem.DespawnLater(z.EventCh)
  39.                                 z.ItemsMap[dropItem.ID] = dropItem
  40.                                 spawnItemEvent := AquireEvent(EventSpawn, dropItem.Pack()...)
  41.                                 z.broadcastZone(spawnItemEvent)
  42.                         }
  43.                         z.Logger.Println("[DEBUG] m", m.ID, "DESPAWN LATER!")
  44.                         m.RespawnLater(z.EventCh)
  45.                         despawnEvent := AquireEvent(EventDespawn, m.ID)
  46.                         z.broadcastZone(despawnEvent)
  47.                         killEvent := AquireEvent(EventKill, m.Kind.ID)
  48.                         _ = p.send(killEvent)
  49.                         z.Logger.Println("[DEBUG] m", m.ID, "DESPAWN!")
  50.                 }
  51.         }
  52. }
复制代码
所有玩家及伤害累积基于当前被攻击的mob的Haters列表,mob选择一个累积伤害最大的玩家举行攻击
9.代码还需美满点


10.三方框架

语言
框架
c
skynet
c++
kbengine/TrinityCore
golang
leaf
rust
veloren


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4