├── .dockerignore ├── .gitignore ├── README.md ├── action ├── main.go └── packets │ └── incoming │ └── inventory.go ├── chat ├── main.go └── packets │ ├── incoming │ └── chat.go │ └── outgoing │ └── chat.go ├── cluster ├── main.go ├── packets.go └── packets │ └── incoming.go ├── common ├── def │ ├── component │ │ ├── moving.go │ │ ├── position.go │ │ └── statistics.go │ └── packet │ │ ├── packettype │ │ └── packettype.go │ │ └── snapshottype │ │ └── snapshottype.go ├── feature │ └── inventory │ │ ├── def │ │ ├── item.go │ │ ├── itembase.go │ │ └── updatetype.go │ │ ├── helpers │ │ └── unequip.go │ │ ├── inventory.go │ │ └── packets │ │ └── outgoing │ │ └── inventory.go └── service │ ├── cache │ ├── database.go │ ├── find.go │ └── player.go │ ├── database │ ├── database.go │ ├── item.go │ └── player.go │ ├── dotenv │ └── dotenv.go │ ├── external │ ├── client.go │ ├── packet.go │ └── server.go │ ├── messaging │ ├── messaging.go │ ├── publisher.go │ ├── subscriber.go │ └── topics.go │ ├── resources │ ├── .gitignore │ ├── defines │ │ └── defines.go │ ├── propitem.go │ └── reader │ │ ├── reader.go │ │ └── types.go │ └── timetick │ └── timetick.go ├── config ├── .env ├── docker.env └── modd │ └── action.conf ├── connectionserver ├── main.go └── service │ └── connectionmanager │ └── connectionmanager.go ├── deps.Dockerfile ├── docker-compose.yml ├── entity ├── main.go └── packets │ ├── incoming │ ├── disconnect.go │ └── join.go │ └── outgoing │ ├── despawn.go │ └── spawn.go ├── go.mod ├── go.sum ├── login ├── main.go ├── packet.go └── servers.go ├── moving ├── def │ └── packets │ │ └── behaviour.go ├── feature │ └── move │ │ ├── destpos.go │ │ ├── move.go │ │ └── save.go ├── main.go └── packets │ ├── incoming │ ├── behaviour.go │ └── move.go │ └── outgoing │ ├── behaviour.go │ └── move.go └── resources ├── define.json ├── defineAttribute.json ├── defineEvent.json ├── defineHonor.json ├── defineItem.json ├── defineItemkind.json ├── defineJob.json ├── defineNeuz.json ├── defineObj.json ├── defineSkill.json ├── defineSound.json ├── defineText.json ├── defineWorld.json ├── definelordskill.json └── definequest.json /.dockerignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | .vscode/ 3 | debug 4 | parser 5 | vendor 6 | bin 7 | resources/* 8 | !resources/define.json -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | .vscode/ 3 | debug 4 | parser 5 | vendor 6 | bin 7 | resources/* 8 | !resources/*.json 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CHECK THE NEW VERSION : https://github.com/steve-nzr/goff 2 | 3 | # GO-FF 4 | 5 | A modern microservices Fly For Fun V15 Emulator. 6 | 7 | ## Quick Start 8 | To get a working environment you'll just need `docker` & `docker-compose` 9 | * Docker : https://docs.docker.com/install/ 10 | * Compose : https://docs.docker.com/compose/install/ 11 | 12 | Then run ... 13 | ```bash 14 | $ docker-compose up -d 15 | ``` 16 | 17 | --- 18 | 19 | Retreive the IP of the VM that executes docker & put it in your client. 20 | For modern Flyff clients with custom launch arguments you can specify this IP at launch 21 | ``` 22 | $ neuz.exe sunkist 127.0.0.1 23 | ``` 24 | 25 | ## Architecture 26 | Here's the complete architecture overview of the project (Open-it with Draw.io) 27 | * https://drive.google.com/file/d/1ouAbHP_F27fSzD8aAxr3OK-l1cX32Mvg/view?usp=sharing 28 | 29 | ### Services 30 | These descriptions relates to ACTUAL state of the services. It might evolve & grow ! Refer to the Architecture diagram to get a more complete description of their usage. 31 | 32 | --- 33 | 34 | * Asynchronous Messaging - Message Broker 35 | * Engine : RabbitMQ 36 | * Persistent SQL Storage 37 | * Engine : PostgreSQL 38 | * Using : Storing player data for a long period 39 | * In-memory Caching SQL Storage 40 | * Engine : MariaDB (MEMORY Db Engine) 41 | * Using : Storing player in-game state for the time they're connected 42 | * Database Administration Tool 43 | * Name : Adminer 44 | * Port : 80 45 | * Game Services 46 | * Out of Game 47 | * Login 48 | * Language : Golang 49 | * Port : 23000 50 | * Stateless : `yes` 51 | * Using : Handling player account login (actually static data are used) 52 | * Cluster 53 | * Language : Golang 54 | * Port : 28000 55 | * Stateless : `yes` 56 | * Using : Handling player character management 57 | * In Game 58 | * ConnectionServer 59 | * Language : Golang 60 | * Port : 5400 61 | * Stateless : `not yet` 62 | * Using 63 | * Handling player connection 64 | * Handling player packets 65 | * Dispatching incoming player packets to the Broker 66 | * Receiving ougoing packets to be broadcaster to players 67 | * Entity 68 | * Language : Golang 69 | * Stateless : `yes` 70 | * Using 71 | * Handling player connection & disconnection 72 | * Broadcasting spawn & despawn events 73 | * Moving 74 | * Language : Golang 75 | * Stateless : `yes` 76 | * Using 77 | * Handling player moves (WASD, Mouse) 78 | * Handling player motions (Jump etc...) 79 | * Chat 80 | * Language : Golang 81 | * Stateless : `yes` 82 | * Using 83 | * Handling player public chat 84 | * Action 85 | * Language : Golang 86 | * Stateless : `yes` 87 | * Using 88 | * Handling player inventory actions 89 | -------------------------------------------------------------------------------- /action/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "go-ff/action/packets/incoming" 6 | "go-ff/common/def/packet/packettype" 7 | "go-ff/common/service/cache" 8 | "go-ff/common/service/dotenv" 9 | "go-ff/common/service/external" 10 | "go-ff/common/service/messaging" 11 | "log" 12 | ) 13 | 14 | func main() { 15 | log.SetFlags(log.LstdFlags | log.Lshortfile) 16 | dotenv.Initialize() 17 | cache.Initialize() 18 | messaging.Initialize() 19 | 20 | log.Println("Server is up !") 21 | 22 | ch := make(chan []byte) 23 | go messaging.Subscribe(messaging.ActionTopic, ch) 24 | 25 | for { 26 | b := <-ch 27 | p := new(external.PacketHandler) 28 | if err := json.Unmarshal(b, p); err != nil { 29 | log.Print(err) 30 | continue 31 | } 32 | 33 | id := p.Packet.ReadUInt32() 34 | switch id { 35 | case packettype.Doequip: 36 | { 37 | incoming.EquipItem(p) 38 | break 39 | } 40 | case packettype.Moveitem: 41 | { 42 | incoming.MoveItem(p) 43 | break 44 | } 45 | case packettype.Dropitem: 46 | { 47 | incoming.DropItem(p) 48 | break 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /action/packets/incoming/inventory.go: -------------------------------------------------------------------------------- 1 | package incoming 2 | 3 | import ( 4 | "go-ff/common/feature/inventory/helpers" 5 | "go-ff/common/service/cache" 6 | "go-ff/common/service/external" 7 | ) 8 | 9 | // EquipItem packet 10 | func EquipItem(p *external.PacketHandler) { 11 | player := cache.FindByNetID(p.ClientID) 12 | if player == nil { 13 | return 14 | } 15 | 16 | uniqueID := p.Packet.ReadUInt32() 17 | part := p.Packet.ReadInt32() 18 | 19 | helpers.Equip(player, (uint8)(uniqueID), part) 20 | } 21 | 22 | // MoveItem packet 23 | func MoveItem(p *external.PacketHandler) { 24 | player := cache.FindByNetID(p.ClientID) 25 | if player == nil { 26 | return 27 | } 28 | 29 | p.Packet.ReadUInt8() // skipped 30 | sourceSlot := p.Packet.ReadUInt8() 31 | destSlot := p.Packet.ReadUInt8() 32 | 33 | helpers.Move(player, sourceSlot, destSlot) 34 | } 35 | 36 | func DropItem(p *external.PacketHandler) { 37 | player := cache.FindByNetID(p.ClientID) 38 | if player == nil { 39 | return 40 | } 41 | 42 | p.Packet.ReadUInt32() 43 | uniqueID := p.Packet.ReadUInt32() 44 | count := p.Packet.ReadInt16() 45 | helpers.Drop(player, uniqueID, count) 46 | } 47 | -------------------------------------------------------------------------------- /chat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "go-ff/chat/packets/incoming" 6 | "go-ff/common/def/packet/packettype" 7 | "go-ff/common/service/cache" 8 | "go-ff/common/service/dotenv" 9 | "go-ff/common/service/external" 10 | "go-ff/common/service/messaging" 11 | "log" 12 | ) 13 | 14 | func main() { 15 | log.SetFlags(log.LstdFlags | log.Lshortfile) 16 | dotenv.Initialize() 17 | cache.Initialize() 18 | messaging.Initialize() 19 | 20 | ch := make(chan []byte) 21 | go messaging.Subscribe(messaging.ChatTopic, ch) 22 | 23 | for { 24 | b := <-ch 25 | p := new(external.PacketHandler) 26 | if err := json.Unmarshal(b, p); err != nil { 27 | log.Print(err) 28 | continue 29 | } 30 | 31 | id := p.Packet.ReadUInt32() 32 | switch id { 33 | case packettype.Chat: 34 | { 35 | incoming.Chat(p) 36 | break 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chat/packets/incoming/chat.go: -------------------------------------------------------------------------------- 1 | package incoming 2 | 3 | import ( 4 | "go-ff/chat/packets/outgoing" 5 | "go-ff/common/service/cache" 6 | "go-ff/common/service/external" 7 | "go-ff/common/service/messaging" 8 | ) 9 | 10 | // Chat packet (Basic chat & commands) 11 | func Chat(p *external.PacketHandler) { 12 | player := cache.FindByNetID(p.ClientID) 13 | if player == nil { 14 | return 15 | } 16 | 17 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 18 | Packet: outgoing.Chat(player, p.Packet.ReadString()), 19 | To: cache.FindIDAround(player), 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /chat/packets/outgoing/chat.go: -------------------------------------------------------------------------------- 1 | package outgoing 2 | 3 | import ( 4 | "go-ff/common/def/packet/snapshottype" 5 | "go-ff/common/service/cache" 6 | "go-ff/common/service/external" 7 | ) 8 | 9 | func Chat(p *cache.Player, msg string) *external.Packet { 10 | return external.StartMergePacket(p.EntityID, snapshottype.Chat, 0xFFFFFF00).WriteString(msg).Finalize() 11 | } 12 | -------------------------------------------------------------------------------- /cluster/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-ff/common/def/packet/packettype" 5 | "go-ff/common/feature/inventory/def" 6 | "go-ff/common/service/database" 7 | "go-ff/common/service/dotenv" 8 | "go-ff/common/service/external" 9 | "go-ff/common/service/resources/defines" 10 | "go-ff/connectionserver/service/connectionmanager" 11 | "log" 12 | 13 | "go-ff/cluster/packets" 14 | ) 15 | 16 | func onConnectedHandler(ch <-chan *external.Client) { 17 | for { 18 | c := <-ch 19 | if c == nil { 20 | continue 21 | } 22 | 23 | connectionmanager.Add(c) 24 | c.SendGreetings() 25 | } 26 | } 27 | 28 | func onDisconnectedHandler(ch <-chan *external.Client) { 29 | for { 30 | c := <-ch 31 | if c == nil { 32 | continue 33 | } 34 | 35 | connectionmanager.Remove(c) 36 | } 37 | } 38 | 39 | func onMessageHandler(ch <-chan *external.PacketHandler) { 40 | for { 41 | p := <-ch 42 | if p == nil { 43 | continue 44 | } 45 | 46 | c := connectionmanager.Get(p.ClientID) 47 | if c == nil { 48 | continue 49 | } 50 | 51 | // Always FFFFFFF 52 | p.Packet.ReadUInt32() 53 | 54 | protocol := p.Packet.ReadUInt32() 55 | 56 | if protocol == packettype.Getplayerlist { 57 | sendPlayerList(c, 0) 58 | sendWorldAddr(c) 59 | } else if protocol == packettype.Create_player { 60 | var createPlayerPacket packets.CreatePlayer 61 | createPlayerPacket.Construct(p.Packet) 62 | 63 | var player database.Player 64 | player.Slot = createPlayerPacket.Slot 65 | player.Name = createPlayerPacket.Name 66 | player.Gender = createPlayerPacket.Gender 67 | player.Position.MapID = 1 68 | player.Position.Vec.X = 6968.0 69 | player.Position.Vec.Y = 100.0 70 | player.Position.Vec.Z = 3328.0 71 | player.SkinSetID = uint32(createPlayerPacket.SkinSet) 72 | player.HairID = uint32(createPlayerPacket.HairMeshID) 73 | player.HairColor = createPlayerPacket.HairColor 74 | player.FaceID = uint32(createPlayerPacket.HeadMesh) 75 | player.JobID = createPlayerPacket.Job 76 | player.Level = 1 77 | player.Statistics.Strength = 15 78 | player.Statistics.Stamina = 15 79 | player.Statistics.Dexterity = 15 80 | player.Statistics.Intelligence = 15 81 | 82 | // Start items 83 | player.Items = append(player.Items, database.Item{ 84 | ItemBase: def.ItemBase{ 85 | Count: 1, 86 | ItemID: (int32)(defines.MustGet("II_WEA_SWO_WOODEN")), 87 | Position: 52, 88 | }, 89 | }) 90 | player.Items = append(player.Items, database.Item{ 91 | ItemBase: def.ItemBase{ 92 | Count: 1, 93 | ItemID: (int32)(defines.MustGet("II_ARM_M_VAG_SUIT01")), 94 | Position: 44, 95 | }, 96 | }) 97 | player.Items = append(player.Items, database.Item{ 98 | ItemBase: def.ItemBase{ 99 | Count: 1, 100 | ItemID: (int32)(defines.MustGet("II_ARM_M_VAG_GAUNTLET01")), 101 | Position: 46, 102 | }, 103 | }) 104 | player.Items = append(player.Items, database.Item{ 105 | ItemBase: def.ItemBase{ 106 | Count: 1, 107 | ItemID: (int32)(defines.MustGet("II_ARM_M_VAG_BOOTS01")), 108 | Position: 47, 109 | }, 110 | }) 111 | 112 | database.Connection.Save(&player) 113 | sendPlayerList(c, 0) 114 | } else if protocol == packettype.Del_player { 115 | var deletePlayerPacket packets.DeletePlayer 116 | deletePlayerPacket.Construct(p.Packet) 117 | 118 | database.Connection.Delete(&database.Player{}, deletePlayerPacket.PlayerID) 119 | sendPlayerList(c, 0) 120 | } else if protocol == packettype.Pre_join { 121 | var preJoinPacket packets.PreJoin 122 | preJoinPacket.Construct(p.Packet) 123 | 124 | c.Send(external.MakePacket(packettype.Pre_join)) 125 | } 126 | } 127 | } 128 | 129 | func main() { 130 | log.SetFlags(log.LstdFlags | log.Lshortfile) 131 | dotenv.Initialize() 132 | database.Initialize() 133 | 134 | // External ---- 135 | onConnected := make(chan *external.Client) 136 | onDisconnected := make(chan *external.Client) 137 | onMessage := make(chan *external.PacketHandler) 138 | 139 | go onConnectedHandler(onConnected) 140 | go onDisconnectedHandler(onDisconnected) 141 | go onMessageHandler(onMessage) 142 | 143 | server := external.Create("0.0.0.0:28000") 144 | server.OnConnected(onConnected) 145 | server.OnDisconnected(onDisconnected) 146 | server.OnMessage(onMessage) 147 | server.Start() 148 | } 149 | -------------------------------------------------------------------------------- /cluster/packets.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-ff/common/def/packet/packettype" 5 | "go-ff/common/feature/inventory" 6 | "go-ff/common/service/database" 7 | "go-ff/common/service/external" 8 | 9 | linq "github.com/ahmetb/go-linq" 10 | ) 11 | 12 | func sendWorldAddr(c *external.Client) { 13 | packet := external.MakePacket(packettype.Cache_addr). 14 | WriteString("192.168.1.38") 15 | 16 | c.Send(packet) 17 | } 18 | 19 | func sendPlayerList(c *external.Client, authKey int32) { 20 | var characters []database.Player 21 | database.Connection.Limit(3).Preload("Items").Find(&characters) 22 | 23 | packet := external.MakePacket(packettype.Player_list). 24 | WriteInt32(0). 25 | WriteInt32(int32(len(characters))) 26 | 27 | for _, c := range characters { 28 | packet.WriteUInt32(uint32(c.Slot)). 29 | WriteUInt32(1). 30 | WriteUInt32(c.Position.MapID). 31 | WriteUInt32(0x0B + uint32(c.Gender)). 32 | WriteString(c.Name). 33 | WriteFloat32(float32(c.Position.Vec.X)). 34 | WriteFloat32(float32(c.Position.Vec.Y)). 35 | WriteFloat32(float32(c.Position.Vec.Z)). 36 | WriteUInt32(uint32(c.ID)). 37 | WriteUInt32(0). 38 | WriteUInt32(0). 39 | WriteUInt32(0). 40 | WriteUInt32(c.SkinSetID). 41 | WriteUInt32(c.HairID). 42 | WriteUInt32(c.HairColor). 43 | WriteUInt32(c.FaceID). 44 | WriteUInt8(c.Gender). 45 | WriteUInt32(uint32(c.JobID)). 46 | WriteUInt32(uint32(c.Level)). 47 | WriteUInt32(0). 48 | WriteUInt32(uint32(c.Statistics.Strength)). 49 | WriteUInt32(uint32(c.Statistics.Stamina)). 50 | WriteUInt32(uint32(c.Statistics.Dexterity)). 51 | WriteUInt32(uint32(c.Statistics.Intelligence)). 52 | WriteUInt32(0) 53 | 54 | var equipedItems []database.Item 55 | linq.From(c.Items). 56 | Where(func(i interface{}) bool { 57 | item := i.(database.Item) 58 | return item.Position > inventory.EquipOffset 59 | }). 60 | Select(func(i interface{}) interface{} { 61 | return i.(database.Item) 62 | }). 63 | ToSlice(&equipedItems) 64 | 65 | packet.WriteUInt32(uint32(len(equipedItems))) 66 | for _, item := range equipedItems { 67 | packet.WriteInt32(item.ItemID) 68 | } 69 | } 70 | 71 | packet.WriteInt32(0) 72 | c.Send(packet) 73 | } 74 | -------------------------------------------------------------------------------- /cluster/packets/incoming.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "go-ff/common/service/external" 5 | ) 6 | 7 | // CreatePlayer packet struct 8 | type CreatePlayer struct { 9 | Username string 10 | Password string 11 | Slot uint8 12 | Name string 13 | FaceID uint8 14 | CostumeID uint8 15 | SkinSet uint8 16 | HairMeshID uint8 17 | HairColor uint32 18 | Gender uint8 19 | Job uint8 20 | HeadMesh uint8 21 | BankPassword int32 22 | AuthenticationKey int32 23 | } 24 | 25 | // DeletePlayer packet struct 26 | type DeletePlayer struct { 27 | Username string 28 | Password string 29 | PasswordConfirm string 30 | PlayerID uint32 31 | } 32 | 33 | // PreJoin packet struct 34 | type PreJoin struct { 35 | Username string 36 | PlayerID uint32 37 | CharacterName string 38 | } 39 | 40 | // Construct ... 41 | func (c *CreatePlayer) Construct(p *external.Packet) { 42 | c.Username = p.ReadString() 43 | c.Password = p.ReadString() 44 | c.Slot = p.ReadUInt8() 45 | c.Name = p.ReadString() 46 | c.FaceID = p.ReadUInt8() 47 | c.CostumeID = p.ReadUInt8() 48 | c.SkinSet = p.ReadUInt8() 49 | c.HairMeshID = p.ReadUInt8() 50 | c.HairColor = p.ReadUInt32() 51 | c.Gender = p.ReadUInt8() 52 | c.Job = p.ReadUInt8() 53 | c.HeadMesh = p.ReadUInt8() 54 | c.BankPassword = p.ReadInt32() 55 | c.AuthenticationKey = p.ReadInt32() 56 | } 57 | 58 | // Construct ... 59 | func (d *DeletePlayer) Construct(p *external.Packet) { 60 | d.Username = p.ReadString() 61 | d.Password = p.ReadString() 62 | d.PasswordConfirm = p.ReadString() 63 | d.PlayerID = p.ReadUInt32() 64 | } 65 | 66 | // Construct ... 67 | func (pj *PreJoin) Construct(p *external.Packet) { 68 | pj.Username = p.ReadString() 69 | pj.PlayerID = p.ReadUInt32() 70 | pj.CharacterName = p.ReadString() 71 | } 72 | -------------------------------------------------------------------------------- /common/def/component/moving.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "github.com/golang/geo/r3" 4 | 5 | // Moving component represents the player's move behaviour 6 | type Moving struct { 7 | Destination r3.Vector `gorm:"embedded"` 8 | Motion uint32 9 | Angle float64 10 | } 11 | -------------------------------------------------------------------------------- /common/def/component/position.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "github.com/golang/geo/r3" 4 | 5 | // Position Component (Including MapID) 6 | type Position struct { 7 | MapID uint32 8 | Vec r3.Vector `gorm:"embedded"` 9 | } 10 | -------------------------------------------------------------------------------- /common/def/component/statistics.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | // Statistics Component (Base stats) 4 | type Statistics struct { 5 | Strength uint16 6 | Stamina uint16 7 | Dexterity uint16 8 | Intelligence uint16 9 | } 10 | -------------------------------------------------------------------------------- /common/def/packet/snapshottype/snapshottype.go: -------------------------------------------------------------------------------- 1 | package snapshottype 2 | 3 | const ( 4 | Chat uint16 = 0x0001 5 | Actmsg uint16 = 0x0002 6 | Createitem uint16 = 0x0003 7 | Moveitem uint16 = 0x0004 8 | Tradeputerror uint16 = 0x0005 9 | Doequip uint16 = 0x0006 10 | Trade uint16 = 0x0007 11 | Tradeput uint16 = 0x0008 12 | Tradepull uint16 = 0x0009 13 | Tradeok uint16 = 0x000a 14 | Tradecancel uint16 = 0x000b 15 | Tradeconsent uint16 = 0x000c 16 | Syncitem uint16 = 0x000d 17 | Setposangle uint16 = 0x000e 18 | Createsfxobj uint16 = 0x000f 19 | Setpos uint16 = 0x0010 20 | Setlevel uint16 = 0x0011 21 | Setexperience uint16 = 0x0012 22 | Damage uint16 = 0x0013 23 | Openshopwnd uint16 = 0x0014 24 | Vendor uint16 = 0x0015 25 | Update_vendor uint16 = 0x0016 26 | Update_mover uint16 = 0x0017 27 | Update_item uint16 = 0x0018 28 | Useskill uint16 = 0x0019 29 | Clear_useskill uint16 = 0x001a 30 | Querygetpos uint16 = 0x001b 31 | Setdestparam uint16 = 0x001c 32 | Resetdestparam uint16 = 0x001d 33 | Setpointparam uint16 = 0x001e 34 | Getpos uint16 = 0x001f 35 | Tradeputgold uint16 = 0x0020 36 | Tradecleargold uint16 = 0x0021 37 | Confirmtrade uint16 = 0x0022 38 | Confirmtradecancel uint16 = 0x0023 39 | Runscriptfunc uint16 = 0x0024 40 | Setskilllevel uint16 = 0x0026 41 | Resurrection_message uint16 = 0x0027 42 | Set_slaughter_point uint16 = 0x0028 43 | Setfxp uint16 = 0x0029 44 | Setflightlevel uint16 = 0x002a 45 | Tradelastconfirm uint16 = 0x002b 46 | Tradelastconfirmok uint16 = 0x002c 47 | School_report uint16 = 0x002d 48 | Gamerate uint16 = 0x002e 49 | Eventmessage uint16 = 0x002f 50 | Duelrequest uint16 = 0x0030 51 | Duelstart uint16 = 0x0031 52 | Duelno uint16 = 0x0032 53 | Duelcancel uint16 = 0x0033 54 | Duelpartyrequest uint16 = 0x0034 55 | Duelpartystart uint16 = 0x0035 56 | Duelpartyno uint16 = 0x0036 57 | Duelpartycancel uint16 = 0x0037 58 | Duelpartyresult uint16 = 0x0038 59 | Setscale uint16 = 0x0039 60 | Removequest uint16 = 0x003a 61 | Moverfocus uint16 = 0x003b 62 | Partymapinfo uint16 = 0x003c 63 | Sm_mode_all uint16 = 0x003e 64 | Sm_mode uint16 = 0x003f 65 | Setfame uint16 = 0x0040 66 | Corrreq uint16 = 0x0041 67 | Pvendor_open uint16 = 0x0042 68 | Pvendor_close uint16 = 0x0043 69 | Register_pvendor_item uint16 = 0x0044 70 | Pvendor_item uint16 = 0x0045 71 | Pvendor_item_num uint16 = 0x0046 72 | Unregister_pvendor_item uint16 = 0x0047 73 | Set_hair uint16 = 0x0048 74 | Querygetdestobj uint16 = 0x0049 75 | Getdestobj uint16 = 0x004a 76 | Setfuel uint16 = 0x004b 77 | Setskillstate uint16 = 0x004c 78 | Changeface uint16 = 0x004d 79 | Monsterprop uint16 = 0x004e 80 | Gmchat uint16 = 0x004f 81 | Putitembank uint16 = 0x0050 82 | Getitembank uint16 = 0x0051 83 | Putgoldbank uint16 = 0x0052 84 | Movebankitem uint16 = 0x0053 85 | Update_bankitem uint16 = 0x0054 86 | Bankisfull uint16 = 0x0055 87 | Bankwindow uint16 = 0x0056 88 | Changebankpass uint16 = 0x0057 89 | Confirmbankpass uint16 = 0x0058 90 | Setstun uint16 = 0x0059 91 | Setpoison uint16 = 0x005a 92 | Setdark uint16 = 0x005b 93 | Sendactmsg uint16 = 0x005c 94 | Resistsmmode uint16 = 0x005d 95 | Commercialelem uint16 = 0x005e 96 | Pushpower uint16 = 0x005f 97 | Environment uint16 = 0x0060 98 | Environmentsnow uint16 = 0x0061 99 | Environmentrain uint16 = 0x0062 100 | Environmentall uint16 = 0x0063 101 | Pk_relation uint16 = 0x0065 102 | Update_player_enemy uint16 = 0x0065 103 | Setduel uint16 = 0x0066 104 | Duelcount uint16 = 0x0067 105 | Addpartyname uint16 = 0x0068 106 | Partychat uint16 = 0x0069 107 | Setstate uint16 = 0x006a 108 | Cmdsetskilllevel uint16 = 0x006b 109 | Activeskill uint16 = 0x006e 110 | Resetbuffskill uint16 = 0x006f 111 | Addfriend uint16 = 0x0070 112 | Addfriendreqest uint16 = 0x0071 113 | Addfriendcancel uint16 = 0x0072 114 | Addgetfriendname uint16 = 0x0073 115 | Addfriendgamejoin uint16 = 0x0074 116 | Removefriend uint16 = 0x0075 117 | Addfrienderror uint16 = 0x0076 118 | Addfriendchangejob uint16 = 0x0077 119 | Addgamejoin uint16 = 0x0078 120 | Addpartychangeleader uint16 = 0x0079 121 | Set_war uint16 = 0x007a 122 | Chatting uint16 = 0x007b 123 | Initskillpoint uint16 = 0x007c 124 | Douseskillpoint uint16 = 0x007d 125 | Commonplace uint16 = 0x007e 126 | End_recovermode uint16 = 0x007f 127 | Wanted_info uint16 = 0x0080 128 | Errorparty uint16 = 0x0081 129 | Partymember uint16 = 0x0082 130 | Partyreqest uint16 = 0x0083 131 | Partyreqestcancel uint16 = 0x0084 132 | Partyexp uint16 = 0x0085 133 | Partymemberjob uint16 = 0x0086 134 | Partymemberlevel uint16 = 0x0087 135 | Partychangetroup uint16 = 0x0088 136 | Partychangename uint16 = 0x0089 137 | Partyskill_call uint16 = 0x008a 138 | Partyskill_blitz uint16 = 0x008b 139 | Partyskill_retreat uint16 = 0x008c 140 | Setpartymode uint16 = 0x008d 141 | Partyskill_spherecircle uint16 = 0x008e 142 | Partychangeitemmode uint16 = 0x008f 143 | Partychangeexpmode uint16 = 0x0090 144 | Set_party_member_param uint16 = 0x0091 145 | Do_escape uint16 = 0x0092 146 | Snoop uint16 = 0x0093 147 | Definedtext1 uint16 = 0x0094 148 | Definedtext uint16 = 0x0095 149 | Gametimer uint16 = 0x0096 150 | Taskbar uint16 = 0x0097 151 | Motion uint16 = 0x0098 152 | Queryplayerstring uint16 = 0x0099 153 | Guild_invite uint16 = 0x009a 154 | Set_guild uint16 = 0x009b 155 | Create_guild uint16 = 0x009c 156 | Destroy_guild uint16 = 0x009d 157 | Guild uint16 = 0x009e 158 | All_guilds uint16 = 0x009f 159 | Text uint16 = 0x00a0 160 | Revival uint16 = 0x00a1 161 | Revival_to_lodestar uint16 = 0x00a2 162 | Revival_to_lodelight uint16 = 0x00a3 163 | Set_stat_level uint16 = 0x00a4 164 | Set_job_level uint16 = 0x00a5 165 | Set_growth_learning_point uint16 = 0x00a6 166 | Set_job_skill uint16 = 0x00a7 167 | Set_near_job_skill uint16 = 0x00a8 168 | Returnsay uint16 = 0x00a9 169 | Createsfxallow uint16 = 0x00aa 170 | Removeitematid uint16 = 0x00ab 171 | Queryequip uint16 = 0x00ac 172 | Returnscorll uint16 = 0x00ad 173 | Settarget uint16 = 0x00ae 174 | Focusobj uint16 = 0x00af 175 | Setquest uint16 = 0x00b0 176 | Flyff_event uint16 = 0x00b2 177 | Set_local_event uint16 = 0x00b3 178 | Setcheerparam uint16 = 0x00b4 179 | Setguildquest uint16 = 0x00b5 180 | Removeguildquest uint16 = 0x00b6 181 | Allaction uint16 = 0x00b7 182 | Diag_text uint16 = 0x00b7 183 | Guildcombat uint16 = 0x00b8 184 | Definedcaption uint16 = 0x00b9 185 | Quest_text_time uint16 = 0x00ba 186 | Queryplayerliststring uint16 = 0x00bb 187 | Chattext uint16 = 0x00bc 188 | Expboxinfo uint16 = 0x00bd 189 | Expboxcolltime uint16 = 0x00be 190 | Expboxcolltimecancel uint16 = 0x00bf 191 | Playerpos uint16 = 0x00c0 192 | Destpos uint16 = 0x00c1 193 | Moversetdestobj uint16 = 0x00c2 194 | Destangle uint16 = 0x00c3 195 | Movingactmsg uint16 = 0x00c4 196 | Setactionpoint uint16 = 0x00c5 197 | Setnavipoint uint16 = 0x00c6 198 | Moverdeath uint16 = 0x00c7 199 | Movercorr uint16 = 0x00c8 200 | Movercorr2 uint16 = 0x00c9 201 | Movermoved uint16 = 0x00ca 202 | Moverbehavior uint16 = 0x00cb 203 | Movermoved2 uint16 = 0x00cc 204 | Moverbehavior2 uint16 = 0x00cd 205 | Moverangle uint16 = 0x00ce 206 | Setmovepattern uint16 = 0x00cf 207 | Shout uint16 = 0x00d0 208 | Playmusic uint16 = 0x00d1 209 | Playsound uint16 = 0x00d2 210 | Modifymode uint16 = 0x00d3 211 | Doapplyuseskill uint16 = 0x00d7 212 | Commonskill uint16 = 0x00dd 213 | Statemode uint16 = 0x00df 214 | Melee_attack uint16 = 0x00e0 215 | Magic_attack uint16 = 0x00e1 216 | Range_attack uint16 = 0x00e2 217 | Sp_attack uint16 = 0x00e3 218 | Melee_attack2 uint16 = 0x00e4 219 | Endskillqueue uint16 = 0x00e5 220 | Postmail uint16 = 0x00e6 221 | Removemail uint16 = 0x00e7 222 | Querymailbox uint16 = 0x00e9 223 | Crime uint16 = 0x00ea 224 | Resurrection uint16 = 0x00eb 225 | Ship_actmsg uint16 = 0x00ec 226 | Summon uint16 = 0x00ed 227 | Motion_arrive uint16 = 0x00ee 228 | Add_obj uint16 = 0x00f0 229 | Del_obj uint16 = 0x00f1 230 | Replace uint16 = 0x00f2 231 | Remove_guild_bank_item uint16 = 0x00f3 232 | Removeallskillinfulence uint16 = 0x00f4 233 | Disguise uint16 = 0x00f5 234 | Nodisguise uint16 = 0x00f6 235 | Tag uint16 = 0x00f7 236 | Removeskillinfulence uint16 = 0x00f8 237 | Addregion uint16 = 0x00f9 238 | Guild_bank_wnd uint16 = 0x00000000 239 | Putitemguildbank uint16 = 0x00000000 240 | Getitemguildbank uint16 = 0x00000000 241 | Guild_logo uint16 = 0x00fb 242 | Guild_contribution uint16 = 0x00fc 243 | Guild_notice uint16 = 0x00fd 244 | Guild_authority uint16 = 0x00fe 245 | Guild_penya uint16 = 0x00ff 246 | Guild_real_penya uint16 = 0x00000000 247 | Guild_changejoblevel uint16 = 0x00000000 248 | Guild_addvote uint16 = 0x00000000 249 | Guild_modifyvote uint16 = 0x00000000 250 | War uint16 = 0x00da 251 | Request_guildrank uint16 = 0x00000000 252 | Sex_change uint16 = 0x00000000 253 | Worldmsg uint16 = 0x00de 254 | Calltheroll uint16 = 0x00000000 255 | Angel uint16 = 0x00000000 256 | Minigame uint16 = 0x00e8 257 | Ultimate uint16 = 0x0100 258 | Exchange uint16 = 0x0101 259 | Pet_call uint16 = 0x0110 260 | Pet_release uint16 = 0x0111 261 | Pet_levelup uint16 = 0x0112 262 | Pet_set_exp uint16 = 0x0113 263 | Pet uint16 = 0x0114 264 | Pet_state uint16 = 0x0115 265 | Pet_feed uint16 = 0x0116 266 | Pet_mill uint16 = 0x0117 267 | Set_speed_factor uint16 = 0x0118 268 | Legendskillup_result uint16 = 0x0120 269 | Eventlua_desc uint16 = 0x0121 270 | Remove_attribute uint16 = 0x0122 271 | Start_collecting uint16 = 0x0123 272 | Stop_collecting uint16 = 0x0124 273 | Gc1to1_nowstate uint16 = 0x0125 274 | Gc1to1_tenderopenwnd uint16 = 0x0126 275 | Gc1to1_tenderview uint16 = 0x0127 276 | Gc1to1_memberlineupopenwnd uint16 = 0x0128 277 | Gc1to1_warresult uint16 = 0x0129 278 | Update_item_ex uint16 = 0x0140 279 | Query_player_data uint16 = 0x0141 280 | Guildlog_view uint16 = 0x0142 281 | Restart_collecting uint16 = 0x0143 282 | Sealchar_req uint16 = 0x0144 283 | Sealcharget_req uint16 = 0x0145 284 | Event_coupon uint16 = 0x0146 285 | Pocket_attribute uint16 = 0x0200 286 | Pocket_add_item uint16 = 0x0201 287 | Pocket_remove_item uint16 = 0x0202 288 | Que_petresurrection_result uint16 = 0x0203 289 | Removesfxobj uint16 = 0x0204 290 | Secretroom_mng_state uint16 = 0x0300 291 | Secretroom_info uint16 = 0x0301 292 | Secretroom_tenderopenwnd uint16 = 0x0302 293 | Secretroom_lineupopenwnd uint16 = 0x0303 294 | Secretroom_tenderview uint16 = 0x0304 295 | Tax_allinfo uint16 = 0x0400 296 | Tax_settaxrate_openwnd uint16 = 0x0401 297 | Election_add_deposit uint16 = 0x8f00 298 | Election_set_pledge uint16 = 0x8f01 299 | Election_inc_vote uint16 = 0x8f02 300 | Election_begin_candidacy uint16 = 0x8f03 301 | Election_begin_vote uint16 = 0x8f04 302 | Election_end_vote uint16 = 0x8f05 303 | Lord uint16 = 0x8f06 304 | L_event uint16 = 0x8f07 305 | L_event_create uint16 = 0x8f08 306 | L_event_initialize uint16 = 0x8f09 307 | Lord_skill_tick uint16 = 0x8f0a 308 | L_event_tick uint16 = 0x8f0b 309 | Set_tutorial_state uint16 = 0x8f0c 310 | Lord_skill_use uint16 = 0x8f0d 311 | Rainbowrace_prevranking_openwnduint16 = 0x9000 312 | Rainbowrace_application_openwnduint16 = 0x9001 313 | Rainbowrace_nowstate uint16 = 0x9002 314 | Rainbowrace_minigamestate uint16 = 0x9003 315 | Rainbowrace_minigameextstate uint16 = 0x9004 316 | Set_pet_name uint16 = 0x9100 317 | Housing_allinfo uint16 = 0x9200 318 | Housing_furniturelist uint16 = 0x9201 319 | Housing_setupfurniture uint16 = 0x9202 320 | Housing_paperinginfo uint16 = 0x9203 321 | Housing_setvisitallow uint16 = 0x9204 322 | Housing_visitablelist uint16 = 0x9205 323 | Honor_list_ack uint16 = 0x9300 324 | Honor_change_ack uint16 = 0x9301 325 | Questhelper_npcpos uint16 = 0x9400 326 | Clear_target uint16 = 0x9500 327 | Couple uint16 = 0x9700 328 | Propose_result uint16 = 0x9701 329 | Couple_result uint16 = 0x9703 330 | Decouple_result uint16 = 0x9704 331 | Add_couple_experience uint16 = 0x9705 332 | Resetdestparam_sync uint16 = 0x9800 333 | Pcbang_info uint16 = 0x9810 334 | Account_playtime uint16 = 0x9820 335 | Smelt_safety uint16 = 0x9900 336 | World_readinfo uint16 = 0x9910 337 | Quiz_state uint16 = 0x9920 338 | Quiz_message uint16 = 0x9921 339 | Quiz_question uint16 = 0x9922 340 | Mover_changesfx uint16 = 0x8800 341 | Vispet_activate uint16 = 0x8801 342 | Guildhouse_packet uint16 = 0x8810 343 | Guildhouse_allinfo uint16 = 0x8812 344 | Guildhouse_remove uint16 = 0x8813 345 | Guildhouse_restpoint uint16 = 0x8814 346 | Quest_checked uint16 = 0x8820 347 | Campus_invite uint16 = 0x8830 348 | Campus_update uint16 = 0x8831 349 | Campus_remove uint16 = 0x8832 350 | Campus_update_point uint16 = 0x8833 351 | Item_select_awakening_value uint16 = 0x8834 352 | Guildhouse_tender_mainwnd uint16 = 0x8840 353 | Guildhouse_tender_infownd uint16 = 0x8841 354 | Guildhouse_tender_result uint16 = 0x8842 355 | Querymailbox_req uint16 = 0x8860 356 | ) 357 | -------------------------------------------------------------------------------- /common/feature/inventory/def/item.go: -------------------------------------------------------------------------------- 1 | package def 2 | 3 | // Item cache structure & table 4 | type Item struct { 5 | ID uint32 `gorm:"primary_key"` 6 | ItemBase `gorm:"embedded"` 7 | PlayerID uint32 8 | } 9 | -------------------------------------------------------------------------------- /common/feature/inventory/def/itembase.go: -------------------------------------------------------------------------------- 1 | package def 2 | 3 | import ( 4 | "go-ff/common/service/external" 5 | ) 6 | 7 | // Item holds in-game item data (not itemprops, not reference to a player) 8 | type ItemBase struct { 9 | ItemID int32 10 | UniqueID int32 11 | Count int16 12 | Position int16 13 | } 14 | 15 | // Serialize the item to the packet 16 | func (item ItemBase) Serialize(p *external.Packet) { 17 | p.WriteInt32(item.ItemID). 18 | WriteUInt32(0). 19 | WriteString("ItemName"). 20 | WriteInt16(item.Count). // m_nItemNum 21 | WriteUInt8(0). // m_nRepairNumber 22 | WriteUInt32(0). // m_nHitPoint 23 | WriteUInt32(0). // m_nRepair 24 | WriteUInt8(0). // m_byFlag 25 | WriteUInt32(0). // m_nAbilityOption 26 | WriteUInt32(0). // m_idGuild 27 | WriteUInt8(0). // m_bItemResist 28 | WriteUInt32(0). // m_nResistAbilityOption 29 | WriteUInt32(0). // m_nResistSMItemId 30 | WriteUInt32(0). // m_vPiercing.size 31 | WriteUInt32(0). // m_vUltimatePiercing.size 32 | WriteUInt32(0). // SetVisKeepTime.size 33 | WriteUInt32(0). // m_bCharged 34 | WriteUInt64(0). // m_iRandomOptItemId 35 | WriteUInt32(0). // m_dwKeepTime 36 | WriteUInt8(0). // bPet 37 | WriteUInt32(0) // m_bTranformVisPet 38 | } 39 | -------------------------------------------------------------------------------- /common/feature/inventory/def/updatetype.go: -------------------------------------------------------------------------------- 1 | package def 2 | 3 | const ( 4 | ItmUpdateCount uint8 = 0x0000 5 | ItmUpdateUpgrade uint8 = 0x0003 6 | ItmUpdateEleupgrade uint8 = 0x0004 7 | ItmUpdateElement uint8 = 0x0005 8 | ItmUpdateSockets uint8 = 0x0006 9 | ItmUpdateCard uint8 = 0x0007 10 | ItmUpdateTicket uint8 = 0x000b 11 | ) 12 | -------------------------------------------------------------------------------- /common/feature/inventory/helpers/unequip.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "go-ff/common/feature/inventory" 5 | "go-ff/common/feature/inventory/def" 6 | "go-ff/common/feature/inventory/packets/outgoing" 7 | "go-ff/common/service/cache" 8 | "go-ff/common/service/external" 9 | "go-ff/common/service/messaging" 10 | "go-ff/common/service/resources" 11 | ) 12 | 13 | func Equip(player *cache.Player, uniqueID uint8, part int32) { 14 | index := player.Inventory.GetItemIndex(uniqueID) 15 | if index < 0 { 16 | return 17 | } 18 | 19 | toEquip := part == -1 20 | if toEquip { 21 | prop := resources.ItemsProp[player.Inventory[index].ItemID] 22 | 23 | equipedItemSlot := prop.Parts + inventory.EquipOffset 24 | equipedItem := &player.Inventory[equipedItemSlot] 25 | if equipedItem.ItemID != -1 { 26 | Unequip(player, (uint8)(equipedItem.UniqueID)) 27 | } 28 | 29 | index := player.Inventory.GetItemIndex(uniqueID) // add check 30 | item := &player.Inventory[index] 31 | 32 | item.ItemBase, equipedItem.ItemBase = equipedItem.ItemBase, item.ItemBase 33 | equipedItem.Position = (int16)(equipedItemSlot) 34 | 35 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 36 | Packet: outgoing.Equip(player, equipedItem, true, (int32)(prop.Parts)).Finalize(), 37 | To: cache.FindIDAround(player), 38 | }) 39 | 40 | } else { 41 | Unequip(player, uniqueID) 42 | } 43 | 44 | cache.Connection.Model(&player).Association("Inventory").Replace(player.Inventory) 45 | } 46 | 47 | func Unequip(player *cache.Player, uniqueID uint8) bool { 48 | index := player.Inventory.GetItemIndex(uniqueID) 49 | if index < 0 { 50 | return false 51 | } 52 | 53 | item := &player.Inventory[index] 54 | if item.Position <= inventory.EquipOffset { 55 | return false 56 | } 57 | 58 | availableSlot := player.Inventory.GetAvailableSlot() 59 | if availableSlot < 0 { 60 | return false 61 | } 62 | 63 | targetItem := &player.Inventory[availableSlot] 64 | parts := resources.ItemsProp[item.ItemID].Parts 65 | 66 | item.ItemBase, targetItem.ItemBase = targetItem.ItemBase, item.ItemBase 67 | targetItem.Position = (int16)(availableSlot) 68 | 69 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 70 | Packet: outgoing.Equip(player, targetItem, false, (int32)(parts)).Finalize(), 71 | To: cache.FindIDAround(player), 72 | }) 73 | 74 | return true 75 | } 76 | 77 | func Move(player *cache.Player, sourceSlot uint8, destSlot uint8) { 78 | if sourceSlot > inventory.EquipOffset || 79 | destSlot > inventory.EquipOffset { 80 | return 81 | } 82 | 83 | sourceItem := &player.Inventory[sourceSlot] 84 | destinationItem := &player.Inventory[destSlot] 85 | if sourceItem.Count < 1 { 86 | return 87 | } 88 | 89 | sourceItem.Position = (int16)(destSlot) 90 | if destinationItem.Position != -1 { 91 | destinationItem.Position = (int16)(sourceSlot) 92 | } 93 | 94 | sourceItem.ItemBase, destinationItem.ItemBase = destinationItem.ItemBase, sourceItem.ItemBase 95 | 96 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 97 | Packet: outgoing.Move(player, sourceSlot, destSlot).Finalize(), 98 | To: []uint32{player.NetClientID}, 99 | }) 100 | 101 | cache.Connection.Save(sourceItem) 102 | cache.Connection.Save(destinationItem) 103 | } 104 | 105 | func Drop(player *cache.Player, uniqueID uint32, count int16) { 106 | if uniqueID > inventory.MaxItems || 107 | count < 1 { 108 | return 109 | } 110 | 111 | index := player.Inventory.GetItemIndex(uint8(uniqueID)) 112 | if index < 0 { 113 | return 114 | } 115 | 116 | item := &player.Inventory[index] 117 | if count >= item.Count { 118 | item.ItemBase = def.ItemBase{ 119 | ItemID: -1, 120 | UniqueID: item.UniqueID, 121 | Count: -1, 122 | Position: -1, 123 | } 124 | } else { 125 | item.Count -= count 126 | } 127 | 128 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 129 | Packet: outgoing.Update(player, item.UniqueID, def.ItmUpdateCount, (int32)(item.Count)).Finalize(), 130 | To: []uint32{player.NetClientID}, 131 | }) 132 | 133 | cache.Connection.Save(item) 134 | } 135 | -------------------------------------------------------------------------------- /common/feature/inventory/inventory.go: -------------------------------------------------------------------------------- 1 | package inventory 2 | 3 | import ( 4 | "go-ff/common/feature/inventory/def" 5 | "go-ff/common/service/database" 6 | "go-ff/common/service/external" 7 | ) 8 | 9 | // ItemContainer represents a list of items with the fixed size of an inventory 10 | type ItemContainer []def.Item 11 | 12 | const ( 13 | RightWeaponSlot = 52 14 | MaxItems = 73 15 | EquipOffset = MaxItems - 31 16 | InventorySize = EquipOffset 17 | MaxHumanParts = MaxItems - EquipOffset 18 | ) 19 | 20 | func (container ItemContainer) GetItemIndex(uniqueID uint8) int32 { 21 | for i, item := range container { 22 | if item.UniqueID == int32(uniqueID) { 23 | return int32(i) 24 | } 25 | } 26 | 27 | return -1 28 | } 29 | 30 | func (container ItemContainer) GetAvailableSlot() int32 { 31 | for i, item := range container { 32 | if item.Position == -1 { 33 | return int32(i) 34 | } 35 | } 36 | 37 | return -1 38 | } 39 | 40 | // InitializeWith database Item list 41 | func (container ItemContainer) InitializeWith(items []database.Item) ItemContainer { 42 | for i := 0; i < MaxItems; i++ { 43 | container = append(container, def.Item{ 44 | ItemBase: def.ItemBase{ 45 | UniqueID: int32(i), 46 | Position: -1, 47 | Count: -1, 48 | ItemID: -1, 49 | }, 50 | }) 51 | } 52 | 53 | for _, item := range items { 54 | if item.Position >= MaxItems || item.Position < 0 { 55 | continue 56 | } 57 | 58 | item.UniqueID = container[item.Position].UniqueID 59 | container[item.Position] = def.Item{ 60 | ItemBase: item.ItemBase, 61 | } 62 | } 63 | 64 | for i := EquipOffset; i < MaxItems; i++ { 65 | if container[i].ItemID == -1 { 66 | container[i].UniqueID = -1 67 | } 68 | } 69 | 70 | return container 71 | } 72 | 73 | // ConvertToDatabaseSlice creates a slice to be savec to the persistent database 74 | func (container ItemContainer) ConvertToDatabaseSlice() []database.Item { 75 | var items []database.Item 76 | for _, item := range container { 77 | if item.ItemID < 1 { 78 | continue 79 | } 80 | 81 | items = append(items, database.Item{ 82 | PlayerID: item.PlayerID, 83 | ItemBase: item.ItemBase, 84 | }) 85 | } 86 | 87 | return items 88 | } 89 | 90 | // Serialize the entire inventory to the given packet 91 | func (container ItemContainer) Serialize(p *external.Packet) { 92 | var size uint8 93 | for i := 0; i < MaxItems; i++ { 94 | p.WriteInt32(container[i].UniqueID) 95 | if container[i].ItemID != -1 { 96 | size++ 97 | } 98 | } 99 | 100 | p.WriteUInt8(size) 101 | 102 | for i := 0; i < MaxItems; i++ { 103 | if container[i].ItemID > 0 { 104 | p.WriteUInt8(uint8(container[i].UniqueID)). 105 | WriteInt32(container[i].UniqueID) 106 | container[i].Serialize(p) 107 | } 108 | } 109 | 110 | for i := 0; i < MaxItems; i++ { 111 | p.WriteInt32(container[i].UniqueID) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /common/feature/inventory/packets/outgoing/inventory.go: -------------------------------------------------------------------------------- 1 | package outgoing 2 | 3 | import ( 4 | "go-ff/common/def/packet/snapshottype" 5 | "go-ff/common/feature/inventory/def" 6 | "go-ff/common/service/cache" 7 | "go-ff/common/service/external" 8 | ) 9 | 10 | func Equip(player *cache.Player, item *def.Item, equip bool, targetSlot int32) *external.Packet { 11 | p := external.StartMergePacket(player.EntityID, snapshottype.Doequip, 0xFFFFFF00). 12 | WriteInt8((int8)(item.UniqueID)). 13 | WriteInt32(0 /* idGuild */) 14 | 15 | if equip { 16 | p.WriteInt8(1) 17 | } else { 18 | p.WriteInt8(0) 19 | } 20 | 21 | p.WriteInt32(item.ItemID). 22 | WriteInt16(0 /* Refine */). 23 | WriteInt8(0 /* Element */). 24 | WriteInt8(0 /* ElementRefine */). 25 | WriteInt32(0) 26 | 27 | p.WriteInt32(targetSlot) // nPart 28 | 29 | return p 30 | } 31 | 32 | func Move(player *cache.Player, sourceSlot uint8, destSlot uint8) *external.Packet { 33 | return external.StartMergePacket(player.EntityID, snapshottype.Moveitem, 0xFFFFFF00). 34 | WriteUInt8(0). 35 | WriteUInt8(sourceSlot). 36 | WriteUInt8(destSlot) 37 | } 38 | 39 | func Update(player *cache.Player, uniqueID int32, updatetype uint8, data int32) *external.Packet { 40 | return external.StartMergePacket(player.EntityID, snapshottype.Update_item, 0xFFFFFF00). 41 | WriteUInt8(0). 42 | WriteUInt8(uint8(uniqueID)). 43 | WriteUInt8(updatetype). 44 | WriteInt32(data). 45 | WriteUInt32(0) 46 | } 47 | -------------------------------------------------------------------------------- /common/service/cache/database.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "go-ff/common/feature/inventory/def" 5 | "log" 6 | "os" 7 | 8 | "github.com/jinzhu/gorm" 9 | // Using with GORM Open 10 | _ "github.com/jinzhu/gorm/dialects/mysql" 11 | ) 12 | 13 | // Connection holds an active connection to the DB 14 | var Connection *gorm.DB 15 | 16 | // Initialize the Database connection & tables 17 | func Initialize() { 18 | db, err := gorm.Open("mysql", os.Getenv("MYSQL_CACHE_ENDPOINT")) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | Connection = db 24 | autoMigrate() 25 | } 26 | 27 | func autoMigrate() { 28 | Connection.Set("gorm:table_options", "ENGINE=Memory").AutoMigrate(&Player{}) 29 | Connection.Set("gorm:table_options", "ENGINE=Memory").AutoMigrate(&def.Item{}) 30 | } 31 | -------------------------------------------------------------------------------- /common/service/cache/find.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | // FindByNetID return the player who has this clientID 8 | func FindByNetID(id uint32) *Player { 9 | player := new(Player) 10 | err := Connection.Preload("Inventory").Where("net_client_id = ?", id).First(player).Error 11 | if err != nil { 12 | log.Print(err) 13 | return nil 14 | } 15 | 16 | return player 17 | } 18 | 19 | // FindIDAroundOnly returns each player'ID around the given one 20 | func FindIDAroundOnly(p *Player) []uint32 { 21 | var IDlist []uint32 22 | err := Connection.Model(&Player{}).Where("posit_map_id = ? AND net_client_id != ?", p.Position.MapID, p.NetClientID).Pluck("net_client_id", &IDlist).Error 23 | if err != nil { 24 | log.Print(err) 25 | return nil 26 | } 27 | 28 | return IDlist 29 | } 30 | 31 | // FindIDAround returns each player'ID around the given one including itself 32 | func FindIDAround(p *Player) []uint32 { 33 | var IDlist []uint32 34 | err := Connection.Model(&Player{}).Where("posit_map_id = ?", p.Position.MapID).Pluck("net_client_id", &IDlist).Error 35 | if err != nil { 36 | log.Print(err) 37 | return nil 38 | } 39 | 40 | return IDlist 41 | } 42 | 43 | // FindAround returns each player structure around the given one including itself 44 | func FindAround(p *Player) []Player { 45 | var playerList []Player 46 | err := Connection.Preload("Inventory").Where("posit_map_id = ?", p.Position.MapID).Find(&playerList).Error 47 | if err != nil { 48 | log.Print(err) 49 | return nil 50 | } 51 | 52 | return playerList 53 | } 54 | 55 | // FindAroundOnly returns each player structure around the given one excluding itself 56 | func FindAroundOnly(p *Player) []Player { 57 | var playerList []Player 58 | err := Connection.Preload("Inventory").Where("posit_map_id = ? AND net_client_id != ?", p.Position.MapID, p.NetClientID).Find(&playerList).Error 59 | if err != nil { 60 | log.Print(err) 61 | return nil 62 | } 63 | 64 | return playerList 65 | } 66 | -------------------------------------------------------------------------------- /common/service/cache/player.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "go-ff/common/def/component" 5 | "go-ff/common/feature/inventory" 6 | 7 | "github.com/jinzhu/gorm" 8 | ) 9 | 10 | // Player structure representing it's State 11 | type Player struct { 12 | EntityID uint32 `gorm:"unique_index"` 13 | NetClientID uint32 `gorm:"primary_key"` 14 | Type uint8 15 | Name string `gorm:"size:64"` 16 | Gender uint8 17 | ModelID uint32 18 | Position component.Position `gorm:"embedded;EMBEDDED_PREFIX:posit_"` 19 | Angle float32 20 | Size uint8 21 | Level uint16 22 | PlayerID uint32 23 | Slot uint8 24 | JobID uint8 25 | HairColor uint32 26 | HairID uint32 27 | SkinSetID uint32 28 | FaceID uint32 29 | Statistics component.Statistics `gorm:"embedded;EMBEDDED_PREFIX:stats_"` 30 | Moving component.Moving `gorm:"embedded;EMBEDDED_PREFIX:movin_"` 31 | Inventory inventory.ItemContainer `gorm:"foreignkey:PlayerID;association_foreignkey:NetClientID"` 32 | } 33 | 34 | // BeforeDelete the player 35 | func (p *Player) BeforeDelete(scope *gorm.Scope) error { 36 | Connection.Where("player_id = ?", p.NetClientID).Delete(&p.Inventory) 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /common/service/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "go-ff/common/feature/inventory/def" 5 | "log" 6 | "os" 7 | 8 | "github.com/jinzhu/gorm" 9 | // Using with GORM Open 10 | _ "github.com/jinzhu/gorm/dialects/postgres" 11 | ) 12 | 13 | // Connection holds an active connection to the DB 14 | var Connection *gorm.DB 15 | 16 | // Initialize the DB connection 17 | func Initialize() { 18 | db, err := gorm.Open("postgres", os.Getenv("POSTGRES_DB_ENDPOINT")) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | Connection = db 24 | autoMigrate() 25 | } 26 | 27 | func autoMigrate() { 28 | Connection.AutoMigrate(&Player{}) 29 | Connection.AutoMigrate(&def.Item{}) 30 | } 31 | -------------------------------------------------------------------------------- /common/service/database/item.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import "go-ff/common/feature/inventory/def" 4 | 5 | // Item database structure & table 6 | type Item struct { 7 | ID uint32 `gorm:"primary_key"` 8 | def.ItemBase `gorm:"embedded"` 9 | PlayerID uint32 10 | } 11 | -------------------------------------------------------------------------------- /common/service/database/player.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "go-ff/common/def/component" 5 | 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | // Player holds a complete account's character/player 10 | type Player struct { 11 | ID uint32 `gorm:"primary_key"` 12 | Slot uint8 13 | Name string `gorm:"type:VARCHAR(32)"` 14 | Gender uint8 15 | SkinSetID uint32 16 | HairID uint32 17 | HairColor uint32 18 | FaceID uint32 19 | JobID uint8 20 | Level uint16 21 | Position component.Position `gorm:"embedded;EMBEDDED_PREFIX:posit_"` 22 | Statistics component.Statistics `gorm:"embedded;EMBEDDED_PREFIX:stats_"` 23 | Items []Item 24 | } 25 | 26 | // BeforeDelete the player 27 | func (p *Player) BeforeDelete(scope *gorm.Scope) error { 28 | Connection.Delete(&p.Items) 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /common/service/dotenv/dotenv.go: -------------------------------------------------------------------------------- 1 | package dotenv 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/joho/godotenv" 8 | ) 9 | 10 | // Initialize the service 11 | // Loads the .env file 12 | func Initialize() { 13 | filename, ok := os.LookupEnv("ENV_FILE") 14 | if ok == false { 15 | filename = ".env" 16 | } 17 | err := godotenv.Load("./config/" + filename) 18 | if err != nil { 19 | log.Print(err.Error()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/service/external/client.go: -------------------------------------------------------------------------------- 1 | package external 2 | 3 | import ( 4 | "go-ff/common/def/packet/packettype" 5 | "log" 6 | "math/rand" 7 | "net" 8 | "time" 9 | ) 10 | 11 | // Client stores the network connection + the ID 12 | type Client struct { 13 | net.Conn 14 | ID uint32 15 | } 16 | 17 | // NewClient returns a Client instance with the given net.Conn 18 | func newClient(c net.Conn) *Client { 19 | nc := new(Client) 20 | nc.Conn = c 21 | nc.ID = GenerateID() 22 | 23 | return nc 24 | } 25 | 26 | // Send a Packet to the Client 27 | func (nc *Client) Send(p *Packet) { 28 | p.Finalize() 29 | 30 | _, err := nc.Write(p.Data[:p.Offset]) 31 | if err != nil { 32 | log.Println(err) 33 | nc.Close() 34 | } 35 | } 36 | 37 | // SendFinalized sends a Packet to the Client 38 | func (nc *Client) SendFinalized(p *Packet) { 39 | _, err := nc.Write(p.Data[:p.Offset]) 40 | if err != nil { 41 | log.Println(err) 42 | nc.Close() 43 | } 44 | } 45 | 46 | // GenerateID "pseudo-random" from time 47 | func GenerateID() uint32 { 48 | src := rand.NewSource(time.Now().UnixNano()) 49 | rnd := rand.New(src) 50 | return rnd.Uint32() 51 | } 52 | 53 | // SendGreetings to the client 54 | func (nc *Client) SendGreetings() *Client { 55 | p := MakePacket(packettype.Welcome). 56 | WriteUInt32(nc.ID) 57 | 58 | nc.Send(p) 59 | return nc 60 | } 61 | -------------------------------------------------------------------------------- /common/service/external/packet.go: -------------------------------------------------------------------------------- 1 | package external 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | "strings" 7 | 8 | "github.com/golang/geo/r3" 9 | ) 10 | 11 | // Packet stores Data & Offset for a in/out message 12 | type Packet struct { 13 | Data []byte 14 | Offset uint32 15 | MergePacketCount uint16 16 | } 17 | 18 | // MakePacket constructs a new packet with the given protocol 19 | func MakePacket(protocol uint32) *Packet { 20 | packet := new(Packet) 21 | packet.Data = make([]byte, 10240) 22 | packet.MergePacketCount = 0 23 | 24 | return packet. 25 | WriteUInt8(0x5E). 26 | WriteUInt32(0). 27 | WriteUInt32(protocol) 28 | } 29 | 30 | // StartMergePacket create a simple packet without protocol & Data 31 | func StartMergePacket(moverID uint32, cmd uint16, mainCmd uint32) *Packet { 32 | packet := new(Packet) 33 | packet.Data = make([]byte, 10240) 34 | packet.MergePacketCount = 1 35 | 36 | return packet. 37 | WriteUInt8(0x5E). 38 | WriteUInt32(0). 39 | WriteUInt32(mainCmd). 40 | WriteUInt32(0). 41 | WriteUInt16(packet.MergePacketCount). // MergePacketCount 42 | WriteUInt32(moverID). 43 | WriteUInt16(uint16(cmd)) 44 | } 45 | 46 | // AddMergePart add a new entry to the packet 47 | func (p *Packet) AddMergePart(protocol uint16, moverID uint32) *Packet { 48 | p.MergePacketCount++ 49 | 50 | lastOffset := p.Offset 51 | p.Offset = 13 52 | p.WriteUInt16(p.MergePacketCount) 53 | p.Offset = lastOffset 54 | 55 | p.WriteUInt32(moverID). 56 | WriteUInt16(uint16(protocol)) 57 | 58 | return p 59 | } 60 | 61 | // Finalize prepares the packet to be send by adding size 62 | func (p *Packet) Finalize() *Packet { 63 | totalLen := p.Offset 64 | if totalLen < 5 { 65 | return p 66 | } 67 | 68 | binary.LittleEndian.PutUint32(p.Data[1:], uint32(totalLen-5)) 69 | return p 70 | } 71 | 72 | // FinalizeForInternal prepares the packet to be sent internally by adding size 73 | func (p *Packet) FinalizeForInternal() *Packet { 74 | totalLen := p.Offset 75 | if totalLen < 5 { 76 | return p 77 | } 78 | 79 | binary.LittleEndian.PutUint32(p.Data[1:], uint32(totalLen-5)) 80 | p.Offset -= (32 / 8) 81 | return p 82 | } 83 | 84 | // WriteFloat32 at the current Offset 85 | func (p *Packet) WriteFloat32(i float32) *Packet { 86 | binary.LittleEndian.PutUint32(p.Data[p.Offset:], math.Float32bits(i)) 87 | p.Offset += (32 / 8) 88 | return p 89 | } 90 | 91 | // WriteInt64 at the current Offset 92 | func (p *Packet) WriteInt64(i int64) *Packet { 93 | binary.LittleEndian.PutUint64(p.Data[p.Offset:], uint64(i)) 94 | p.Offset += (64 / 8) 95 | return p 96 | } 97 | 98 | // WriteUInt64 at the current Offset 99 | func (p *Packet) WriteUInt64(i uint64) *Packet { 100 | binary.LittleEndian.PutUint64(p.Data[p.Offset:], i) 101 | p.Offset += (64 / 8) 102 | return p 103 | } 104 | 105 | // WriteInt32 at the current Offset 106 | func (p *Packet) WriteInt32(i int32) *Packet { 107 | binary.LittleEndian.PutUint32(p.Data[p.Offset:], uint32(i)) 108 | p.Offset += (32 / 8) 109 | return p 110 | } 111 | 112 | // WriteUInt32 at the current Offset 113 | func (p *Packet) WriteUInt32(i uint32) *Packet { 114 | binary.LittleEndian.PutUint32(p.Data[p.Offset:], i) 115 | p.Offset += (32 / 8) 116 | return p 117 | } 118 | 119 | // WriteInt16 at the current Offset 120 | func (p *Packet) WriteInt16(i int16) *Packet { 121 | binary.LittleEndian.PutUint16(p.Data[p.Offset:], uint16(i)) 122 | p.Offset += (16 / 8) 123 | return p 124 | } 125 | 126 | // WriteUInt16 at the current Offset 127 | func (p *Packet) WriteUInt16(i uint16) *Packet { 128 | binary.LittleEndian.PutUint16(p.Data[p.Offset:], i) 129 | p.Offset += (16 / 8) 130 | return p 131 | } 132 | 133 | // WriteInt8 at the current Offset 134 | func (p *Packet) WriteInt8(i int8) *Packet { 135 | p.Data[p.Offset] = uint8(i) 136 | p.Offset += (8 / 8) 137 | return p 138 | } 139 | 140 | // WriteUInt8 at the current Offset 141 | func (p *Packet) WriteUInt8(i uint8) *Packet { 142 | p.Data[p.Offset] = i 143 | p.Offset += (8 / 8) 144 | return p 145 | } 146 | 147 | // Write3DVector at the current Offset 148 | func (p *Packet) Write3DVector(vec *r3.Vector) *Packet { 149 | return p.WriteFloat32(float32(vec.X)). 150 | WriteFloat32(float32(vec.Y)). 151 | WriteFloat32(float32(vec.Z)) 152 | } 153 | 154 | // WriteString (size+string) at the current Offset 155 | func (p *Packet) WriteString(s string) *Packet { 156 | length := len(s) 157 | if length < 1 { 158 | return p 159 | } 160 | 161 | p.WriteInt32(int32(length)) 162 | for i := 0; i < length; i++ { 163 | p = p.WriteUInt8(s[i]) 164 | } 165 | 166 | return p 167 | } 168 | 169 | // ReadPacket create a new packet instance with the given input Data 170 | func ReadPacket(d []byte) *Packet { 171 | p := new(Packet) 172 | p.Data = d 173 | 174 | return p 175 | } 176 | 177 | // ReadFloat32 at the current Offset 178 | func (p *Packet) ReadFloat32() float32 { 179 | i := binary.LittleEndian.Uint32(p.Data[p.Offset:]) 180 | p.Offset += (32 / 8) 181 | return math.Float32frombits(i) 182 | } 183 | 184 | // ReadInt64 at the current Offset 185 | func (p *Packet) ReadInt64() int64 { 186 | i := binary.LittleEndian.Uint64(p.Data[p.Offset:]) 187 | p.Offset += (64 / 8) 188 | return int64(i) 189 | } 190 | 191 | // ReadUInt64 at the current Offset 192 | func (p *Packet) ReadUInt64() uint64 { 193 | i := binary.LittleEndian.Uint64(p.Data[p.Offset:]) 194 | p.Offset += (64 / 8) 195 | return i 196 | } 197 | 198 | // ReadInt32 at the current Offset 199 | func (p *Packet) ReadInt32() int32 { 200 | i := binary.LittleEndian.Uint32(p.Data[p.Offset:]) 201 | p.Offset += (32 / 8) 202 | return int32(i) 203 | } 204 | 205 | // ReadUInt32 at the current Offset 206 | func (p *Packet) ReadUInt32() uint32 { 207 | i := binary.LittleEndian.Uint32(p.Data[p.Offset:]) 208 | p.Offset += (32 / 8) 209 | return i 210 | } 211 | 212 | // ReadInt16 at the current Offset 213 | func (p *Packet) ReadInt16() int16 { 214 | i := binary.LittleEndian.Uint16(p.Data[p.Offset:]) 215 | p.Offset += (16 / 8) 216 | return int16(i) 217 | } 218 | 219 | // ReadUInt16 at the current Offset 220 | func (p *Packet) ReadUInt16() uint16 { 221 | i := binary.LittleEndian.Uint16(p.Data[p.Offset:]) 222 | p.Offset += (16 / 8) 223 | return i 224 | } 225 | 226 | // ReadInt8 at the current Offset 227 | func (p *Packet) ReadInt8() int8 { 228 | i := p.Data[p.Offset] 229 | p.Offset += (8 / 8) 230 | return int8(i) 231 | } 232 | 233 | // ReadUInt8 at the current Offset 234 | func (p *Packet) ReadUInt8() uint8 { 235 | i := p.Data[p.Offset] 236 | p.Offset += (8 / 8) 237 | return i 238 | } 239 | 240 | // Read3DVector at the current Offset 241 | func (p *Packet) Read3DVector() *r3.Vector { 242 | vec := new(r3.Vector) 243 | vec.X = float64(p.ReadFloat32()) 244 | vec.Y = float64(p.ReadFloat32()) 245 | vec.Z = float64(p.ReadFloat32()) 246 | return vec 247 | } 248 | 249 | // ReadString (size+string) at the current Offset 250 | func (p *Packet) ReadString() string { 251 | var buffer strings.Builder 252 | len := p.ReadUInt32() 253 | 254 | for i := uint32(0); i < len; i++ { 255 | buffer.WriteByte(p.ReadUInt8()) 256 | } 257 | 258 | return buffer.String() 259 | } 260 | -------------------------------------------------------------------------------- /common/service/external/server.go: -------------------------------------------------------------------------------- 1 | package external 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "time" 7 | ) 8 | 9 | // PacketHandler represents an incoming packet from a client 10 | type PacketHandler struct { 11 | ClientID uint32 12 | Packet *Packet 13 | } 14 | 15 | // PacketEmitter represents an outgoing packet to the ConnectionService 16 | type PacketEmitter struct { 17 | To []uint32 18 | Packet *Packet 19 | } 20 | 21 | // Server represents a TCP server 22 | type Server struct { 23 | connConnected chan<- *Client 24 | connDisconnected chan<- *Client 25 | connMessage chan<- *PacketHandler 26 | host string 27 | } 28 | 29 | // Create a new Server 30 | func Create(host string) *Server { 31 | server := new(Server) 32 | server.host = host 33 | 34 | return server 35 | } 36 | 37 | // OnConnected attach the specified function to the OnConnected event 38 | func (ns *Server) OnConnected(c chan<- *Client) *Server { 39 | ns.connConnected = c 40 | return ns 41 | } 42 | 43 | // OnDisconnected attach the specified function to the OnDisconnected event 44 | func (ns *Server) OnDisconnected(c chan<- *Client) *Server { 45 | ns.connDisconnected = c 46 | return ns 47 | } 48 | 49 | // OnMessage attach the specified function to the OnMessage event 50 | func (ns *Server) OnMessage(c chan<- *PacketHandler) *Server { 51 | ns.connMessage = c 52 | return ns 53 | } 54 | 55 | // Start the server 56 | func (ns *Server) Start() { 57 | l, err := net.Listen("tcp", ns.host) 58 | if err != nil { 59 | panic(err.Error) 60 | } 61 | defer l.Close() 62 | 63 | for { 64 | conn, err := l.Accept() 65 | if err != nil { 66 | log.Println("Error accepting: ", err.Error()) 67 | continue 68 | } 69 | 70 | go ns.handleClient(conn) 71 | } 72 | } 73 | 74 | func (ns *Server) handleClient(c net.Conn) { 75 | netClient := newClient(c) 76 | ns.connConnected <- netClient 77 | 78 | defer func() { 79 | ns.connDisconnected <- netClient 80 | netClient.Close() 81 | }() 82 | 83 | for { 84 | buf := make([]byte, 2048) 85 | readLength, err := netClient.Read(buf) 86 | if err != nil || readLength < 1 { 87 | break 88 | } 89 | 90 | go func() { 91 | var i uint32 92 | 93 | for { 94 | if i >= uint32(readLength) { 95 | break 96 | } 97 | 98 | packet := ReadPacket(buf[i:]) 99 | if packet.ReadUInt8() != 0x5E { 100 | break 101 | } 102 | 103 | // CHECKSUM SKIP 104 | packet.ReadUInt32() 105 | 106 | packetLen := packet.ReadUInt32() 107 | i += packetLen + 13 108 | nextBuf := buf[i:] 109 | if i < uint32(readLength) && len(nextBuf) > 0 && nextBuf[0] != 0x5E { 110 | break 111 | } 112 | 113 | // CHECKSUM SKIP 114 | packet.ReadUInt32() 115 | packetHandler := new(PacketHandler) 116 | packetHandler.ClientID = netClient.ID 117 | packetHandler.Packet = packet 118 | ns.connMessage <- packetHandler 119 | } 120 | }() 121 | 122 | time.Sleep(5 * time.Millisecond) 123 | 124 | if netClient.Conn == nil { 125 | return 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /common/service/messaging/messaging.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/streadway/amqp" 8 | ) 9 | 10 | var connection *amqp.Connection 11 | var channels map[string]*amqp.Channel 12 | 13 | var messageExpiration = "5000" 14 | 15 | // Initialize the AMQP Connection 16 | func Initialize() { 17 | conn, err := amqp.Dial(os.Getenv("AMQP_ENDPOINT")) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | connection = conn 23 | initializePubTopics() 24 | 25 | messageExpiration = os.Getenv("MESSAGE_EXPIRATION") 26 | } 27 | -------------------------------------------------------------------------------- /common/service/messaging/publisher.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | "github.com/streadway/amqp" 8 | ) 9 | 10 | // Publish a messager to the given topic 11 | // The data parameter needs to be a pointer to type 12 | func Publish(topic string, data interface{}) { 13 | ch, err := connection.Channel() 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | bytes, err := json.Marshal(data) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | err = ch.Publish(topic, "abc", false, false, amqp.Publishing{ 24 | Body: bytes, 25 | }) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/service/messaging/subscriber.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | // Subscribe to all messages in the given topic 8 | // Warning, this is a blocking function 9 | // Consider call it in a Goroutine 10 | func Subscribe(topic string, msgs chan<- []byte) { 11 | ch, err := connection.Channel() 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | 16 | q, err := ch.QueueDeclare( 17 | "q_"+topic, // name (random) 18 | true, // durable 19 | false, // delete when usused 20 | false, // exclusive 21 | false, // no-wait 22 | nil, // arguments 23 | ) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | err = ch.QueueBind( 29 | q.Name, // queue name 30 | "abc", // routing key 31 | topic, // exchange 32 | false, 33 | nil) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | consumeChan, err := ch.Consume( 39 | q.Name, // queue 40 | "", // consumer 41 | false, // auto ack 42 | false, // exclusive 43 | false, // no local 44 | false, // no wait 45 | nil, // args 46 | ) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | for { 52 | msg := <-consumeChan 53 | msg.Ack(true) 54 | msgs <- msg.Body 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /common/service/messaging/topics.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import "log" 4 | 5 | const ( 6 | // ConnectionTopic name 7 | ConnectionTopic string = "connection" 8 | // EntityTopic name 9 | EntityTopic string = "entity" 10 | // ChatTopic name 11 | ChatTopic string = "chat" 12 | // MovingTopic name 13 | MovingTopic string = "moving" 14 | // ActionTopic name 15 | ActionTopic string = "action" 16 | ) 17 | 18 | var pubexchangesname = []string{ 19 | ConnectionTopic, 20 | EntityTopic, 21 | ChatTopic, 22 | MovingTopic, 23 | ActionTopic, 24 | } 25 | 26 | func initializePubTopics() { 27 | ch, err := connection.Channel() 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | for _, name := range pubexchangesname { 33 | err := ch.ExchangeDeclare( 34 | name, // name 35 | "topic", // type 36 | true, // durable 37 | false, // auto-deleted 38 | false, // internal 39 | false, // no-wait 40 | nil, // arguments 41 | ) 42 | 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/service/resources/.gitignore: -------------------------------------------------------------------------------- 1 | converter -------------------------------------------------------------------------------- /common/service/resources/defines/defines.go: -------------------------------------------------------------------------------- 1 | package defines 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | // List of all defines 11 | var List = load() 12 | 13 | func load() (list map[string]int64) { 14 | list = make(map[string]int64) 15 | files, err := ioutil.ReadDir("resources/") 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | for _, file := range files { 21 | if !strings.HasPrefix(file.Name(), "define") && !strings.HasSuffix(file.Name(), ".json") { 22 | continue 23 | } 24 | 25 | b, err := ioutil.ReadFile(fmt.Sprintf("resources/%s", file.Name())) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | fileDefines := make(map[string]int64) 31 | json.Unmarshal(b, &fileDefines) 32 | for k, v := range fileDefines { 33 | if _, ok := list[k]; ok { 34 | panic(fmt.Errorf("define %v already used", k)) 35 | } 36 | list[k] = v 37 | } 38 | 39 | } 40 | 41 | return list 42 | } 43 | 44 | // Get a value by key & return it with the ok value (found or not) 45 | func Get(key string) (v int64, ok bool) { 46 | v, ok = List[key] 47 | return 48 | } 49 | 50 | // MustGet returns a value from a key or panic 51 | func MustGet(key string) int64 { 52 | v, ok := List[key] 53 | if !ok { 54 | panic(fmt.Errorf("defines key %s not found", key)) 55 | } 56 | return v 57 | } 58 | -------------------------------------------------------------------------------- /common/service/resources/propitem.go: -------------------------------------------------------------------------------- 1 | package resources 2 | 3 | import ( 4 | "bufio" 5 | "go-ff/common/service/resources/reader" 6 | "strings" 7 | ) 8 | 9 | var ItemsProp = loadPropItem() 10 | 11 | type ElementPropType uint8 12 | 13 | const ( 14 | ElementNone ElementPropType = iota 15 | ElementFire 16 | ElementWater 17 | ElementElectricity 18 | ElementWind 19 | ElementEarth 20 | ) 21 | 22 | type ObjProp struct { 23 | ID int32 24 | Name string 25 | Type uint64 26 | AI uint64 27 | HP uint64 28 | } 29 | 30 | type CtrlProp struct { 31 | ObjProp 32 | 33 | // Custom fields 34 | CtrlKind1 uint64 35 | CtrlKind2 uint64 36 | CtrlKind3 uint64 37 | SFXCtrl uint64 38 | SndDamage uint64 39 | } 40 | 41 | type ItemProp struct { 42 | CtrlProp 43 | 44 | // Custom fields 45 | Version uint16 46 | Num uint64 47 | PackMax uint64 48 | ItemKind1 uint32 // defineKind.h 49 | ItemKind2 uint32 // defineKind.h 50 | ItemKind3 uint32 // defineKind.h 51 | Job uint32 // defineJob.h 52 | Permanence bool 53 | Useable uint64 54 | Sex uint8 55 | Cost uint64 56 | Endurance uint64 57 | Log int32 58 | Abrasion int32 59 | MaxRepair int32 60 | Handed uint32 // define.h 61 | Flag uint64 62 | Parts uint32 // define.h 63 | Partsub uint32 // define.h (Earring etc..) 64 | PartsFile bool 65 | Exclusive uint32 // define.h 66 | BasePartsIgnore uint32 // define.h 67 | LV uint64 68 | Rare uint64 69 | ShopAble bool 70 | ShellQuantity uint64 71 | ActiveSkillLv uint64 72 | SpellType uint64 73 | LinkKindBullet uint32 // define.h 74 | LinkKind uint32 // define.h 75 | AbilityMin uint64 76 | AbilityMax uint64 77 | Charged bool 78 | ElementType ElementPropType // (NONE, ELECTRICITY.........) 79 | ItemEatk int16 80 | Parry uint64 81 | BlockRating uint64 82 | AddSkillMin int32 83 | AddSkillMax int32 84 | AtkStyle uint64 85 | WeaponType uint32 // define.h 86 | ItemAtkOrder1 uint32 // define.h 87 | ItemAtkOrder2 uint32 // define.h 88 | ItemAtkOrder3 uint32 // define.h 89 | ItemAtkOrder4 uint32 // define.h 90 | ContinuousPainTime uint64 91 | Recoil uint64 92 | LoadingTime uint64 93 | AdjHitRate int64 94 | AttackSpeed float64 95 | DmgShift uint64 96 | AttackRange uint32 // define.h 97 | Probability int32 98 | DestParam [3]uint32 // define.h 99 | AdjParamVal [3]int64 100 | ChgParamVal [3]uint64 101 | DestData1 [3]int64 102 | ActiveSkill uint32 // define.h 103 | ActiveSkillRate uint64 104 | ReqMp uint64 105 | ReqFp uint64 106 | ReqDisLV uint64 107 | ReSkill1 uint64 108 | ReSkillLevel1 uint64 109 | ReSkill2 uint64 110 | ReSkillLevel2 uint64 111 | SkillReadyType uint64 112 | SkillReady uint64 113 | SkillRange uint64 114 | SfxElemental uint64 115 | SfxObj uint32 // define.h 116 | SfxObj2 uint32 // define.h 117 | SfxObj3 uint32 // define.h 118 | SfxObj4 uint32 // define.h 119 | SfxObj5 uint32 // define.h 120 | UseMotion uint32 // define.h 121 | CircleTime uint64 122 | SkillTime uint64 123 | ExeTarget uint64 124 | UseChance uint32 // define.h 125 | SpellRegion uint64 126 | ReferStat1 uint32 // define.h 127 | ReferStat2 uint32 // define.h 128 | ReferTarget1 uint32 // define.h 129 | ReferTarget2 uint32 // define.h 130 | ReferValue1 uint64 131 | ReferValue2 uint64 132 | SkillType uint64 133 | ItemResistElecricity int32 134 | ItemResistDark int32 135 | ItemResistFire int32 136 | ItemResistWind int32 137 | ItemResistWater int32 138 | ItemResistEarth int32 139 | Evildoing int64 140 | ExpertLV uint32 141 | ExpertMax uint64 142 | SubDefine uint32 // define.h 143 | Exp uint64 144 | ComboStyle uint64 145 | FlightSpeed float64 146 | FlightLRAngle float64 147 | FlightTBAngle float64 148 | FlightLimit uint64 149 | FFuelReMax uint64 150 | AFuelReMax uint64 151 | FuelRe uint64 152 | LimitLevel1 uint64 153 | Reflect int32 154 | SndAttack1 uint32 // define.h 155 | SndAttack2 uint32 // define.h 156 | QuestID uint64 157 | TextFileName string 158 | } 159 | 160 | func fillField(prop *ItemProp, index int, field string, r *reader.Reader) { 161 | switch index { 162 | case 0: 163 | prop.Version = r.GetUInt16(field) 164 | case 1: 165 | prop.ID = r.GetInt32(field) 166 | case 2: 167 | prop.Name = field // r.GetUInt32(field) 168 | case 3: 169 | prop.Num = r.GetUInt64(field) 170 | case 4: 171 | prop.PackMax = r.GetUInt64(field) 172 | case 5: 173 | prop.ItemKind1 = r.GetUInt32(field) 174 | case 6: 175 | prop.ItemKind2 = r.GetUInt32(field) 176 | case 7: 177 | prop.ItemKind3 = r.GetUInt32(field) 178 | case 8: 179 | prop.Job = r.GetUInt32(field) 180 | case 9: 181 | prop.Permanence = r.GetBool(field) 182 | case 10: 183 | prop.Useable = r.GetUInt64(field) 184 | case 11: 185 | prop.Sex = r.GetUInt8(field) 186 | case 12: 187 | prop.Cost = r.GetUInt64(field) 188 | case 13: 189 | prop.Endurance = r.GetUInt64(field) 190 | case 14: 191 | prop.Abrasion = r.GetInt32(field) 192 | case 15: 193 | prop.MaxRepair = r.GetInt32(field) 194 | case 16: 195 | prop.Handed = r.GetUInt32(field) 196 | case 17: 197 | prop.Flag = r.GetUInt64(field) 198 | case 18: 199 | prop.Parts = r.GetUInt32(field) 200 | case 19: 201 | prop.Partsub = r.GetUInt32(field) 202 | case 20: 203 | prop.PartsFile = r.GetBool(field) 204 | case 21: 205 | prop.Exclusive = r.GetUInt32(field) 206 | case 22: 207 | prop.BasePartsIgnore = r.GetUInt32(field) 208 | case 23: 209 | prop.LV = r.GetUInt64(field) 210 | case 24: 211 | prop.Rare = r.GetUInt64(field) 212 | case 25: 213 | prop.ShopAble = r.GetBool(field) 214 | case 26: 215 | prop.Log = r.GetInt32(field) 216 | case 27: 217 | prop.Charged = r.GetBool(field) 218 | case 28: 219 | prop.LinkKindBullet = r.GetUInt32(field) 220 | case 29: 221 | prop.LinkKind = r.GetUInt32(field) 222 | case 30: 223 | prop.AbilityMin = r.GetUInt64(field) 224 | case 31: 225 | prop.AbilityMax = r.GetUInt64(field) 226 | case 32: 227 | prop.ElementType = (ElementPropType)(r.GetUInt8(field)) 228 | case 33: 229 | prop.ItemEatk = r.GetInt16(field) 230 | case 34: 231 | prop.Parry = r.GetUInt64(field) 232 | case 35: 233 | prop.BlockRating = r.GetUInt64(field) 234 | case 36: 235 | prop.AddSkillMin = r.GetInt32(field) 236 | case 37: 237 | prop.AddSkillMax = r.GetInt32(field) 238 | case 38: 239 | prop.AtkStyle = r.GetUInt64(field) 240 | case 39: 241 | prop.WeaponType = r.GetUInt32(field) 242 | case 40: 243 | prop.ItemAtkOrder1 = r.GetUInt32(field) 244 | case 41: 245 | prop.ItemAtkOrder2 = r.GetUInt32(field) 246 | case 42: 247 | prop.ItemAtkOrder3 = r.GetUInt32(field) 248 | case 43: 249 | prop.ItemAtkOrder4 = r.GetUInt32(field) 250 | case 44: 251 | prop.ContinuousPainTime = r.GetUInt64(field) 252 | case 45: 253 | prop.ShellQuantity = r.GetUInt64(field) 254 | case 46: 255 | prop.Recoil = r.GetUInt64(field) 256 | case 47: 257 | prop.LoadingTime = r.GetUInt64(field) 258 | case 48: 259 | prop.AdjHitRate = r.GetInt64(field) 260 | case 49: 261 | prop.AttackSpeed = r.GetFloat64(field) 262 | case 50: 263 | prop.DmgShift = r.GetUInt64(field) 264 | case 51: 265 | prop.AttackRange = r.GetUInt32(field) 266 | case 52: 267 | prop.Probability = r.GetInt32(field) 268 | case 53: 269 | prop.DestParam[0] = r.GetUInt32(field) 270 | case 54: 271 | prop.DestParam[1] = r.GetUInt32(field) 272 | case 55: 273 | prop.DestParam[2] = r.GetUInt32(field) 274 | case 56: 275 | prop.AdjParamVal[0] = r.GetInt64(field) 276 | case 57: 277 | prop.AdjParamVal[1] = r.GetInt64(field) 278 | case 58: 279 | prop.AdjParamVal[2] = r.GetInt64(field) 280 | case 59: 281 | prop.ChgParamVal[0] = r.GetUInt64(field) 282 | case 60: 283 | prop.ChgParamVal[1] = r.GetUInt64(field) 284 | case 61: 285 | prop.ChgParamVal[2] = r.GetUInt64(field) 286 | case 62: 287 | prop.DestData1[0] = r.GetInt64(field) 288 | case 63: 289 | prop.DestData1[1] = r.GetInt64(field) 290 | case 64: 291 | prop.DestData1[2] = r.GetInt64(field) 292 | case 65: 293 | prop.ActiveSkill = r.GetUInt32(field) 294 | case 66: 295 | prop.ActiveSkillLv = r.GetUInt64(field) 296 | case 67: 297 | prop.ActiveSkillRate = r.GetUInt64(field) 298 | case 68: 299 | prop.ReqMp = r.GetUInt64(field) 300 | case 69: 301 | prop.ReqFp = r.GetUInt64(field) 302 | case 70: 303 | prop.ReqDisLV = r.GetUInt64(field) 304 | case 71: 305 | prop.ReSkill1 = r.GetUInt64(field) 306 | case 72: 307 | prop.ReSkillLevel1 = r.GetUInt64(field) 308 | case 73: 309 | prop.ReSkill2 = r.GetUInt64(field) 310 | case 74: 311 | prop.ReSkillLevel2 = r.GetUInt64(field) 312 | case 75: 313 | prop.SkillReadyType = r.GetUInt64(field) 314 | case 76: 315 | prop.SkillReady = r.GetUInt64(field) 316 | case 77: 317 | prop.SkillRange = r.GetUInt64(field) 318 | case 78: 319 | prop.SfxElemental = r.GetUInt64(field) 320 | case 79: 321 | prop.SfxObj = r.GetUInt32(field) 322 | case 80: 323 | prop.SfxObj2 = r.GetUInt32(field) 324 | case 81: 325 | prop.SfxObj3 = r.GetUInt32(field) 326 | case 82: 327 | prop.SfxObj4 = r.GetUInt32(field) 328 | case 83: 329 | prop.SfxObj5 = r.GetUInt32(field) 330 | case 84: 331 | prop.UseMotion = r.GetUInt32(field) 332 | case 85: 333 | prop.CircleTime = r.GetUInt64(field) 334 | case 86: 335 | prop.SkillTime = r.GetUInt64(field) 336 | case 87: 337 | prop.ExeTarget = r.GetUInt64(field) 338 | case 88: 339 | prop.UseChance = r.GetUInt32(field) 340 | case 89: 341 | prop.SpellRegion = r.GetUInt64(field) 342 | case 90: 343 | prop.SpellType = r.GetUInt64(field) 344 | case 91: 345 | prop.ReferStat1 = r.GetUInt32(field) 346 | case 92: 347 | prop.ReferStat2 = r.GetUInt32(field) 348 | case 93: 349 | prop.ReferTarget1 = r.GetUInt32(field) 350 | case 94: 351 | prop.ReferTarget2 = r.GetUInt32(field) 352 | case 95: 353 | prop.ReferValue1 = r.GetUInt64(field) 354 | case 96: 355 | prop.ReferValue2 = r.GetUInt64(field) 356 | case 97: 357 | prop.SkillType = r.GetUInt64(field) 358 | case 98: 359 | prop.ItemResistElecricity = r.GetInt32(field) 360 | case 99: 361 | prop.ItemResistFire = r.GetInt32(field) 362 | case 100: 363 | prop.ItemResistWind = r.GetInt32(field) 364 | case 101: 365 | prop.ItemResistWater = r.GetInt32(field) 366 | case 102: 367 | prop.ItemResistEarth = r.GetInt32(field) 368 | case 103: 369 | prop.Evildoing = r.GetInt64(field) 370 | case 104: 371 | prop.ExpertLV = r.GetUInt32(field) 372 | case 105: 373 | prop.ExpertMax = r.GetUInt64(field) 374 | case 106: 375 | prop.SubDefine = r.GetUInt32(field) 376 | case 107: 377 | prop.Exp = r.GetUInt64(field) 378 | case 108: 379 | prop.ComboStyle = r.GetUInt64(field) 380 | case 109: 381 | prop.FlightSpeed = r.GetFloat64(field) 382 | case 110: 383 | prop.FlightLRAngle = r.GetFloat64(field) 384 | case 111: 385 | prop.FlightTBAngle = r.GetFloat64(field) 386 | case 112: 387 | prop.FlightLimit = r.GetUInt64(field) 388 | case 113: 389 | prop.FFuelReMax = r.GetUInt64(field) 390 | case 114: 391 | prop.AFuelReMax = r.GetUInt64(field) 392 | case 115: 393 | prop.FuelRe = r.GetUInt64(field) 394 | case 116: 395 | prop.LimitLevel1 = r.GetUInt64(field) 396 | case 117: 397 | prop.Reflect = r.GetInt32(field) 398 | case 118: 399 | prop.SndAttack1 = r.GetUInt32(field) 400 | case 119: 401 | prop.SndAttack2 = r.GetUInt32(field) 402 | case 120: 403 | prop.QuestID = r.GetUInt64(field) 404 | case 121: 405 | prop.TextFileName = field 406 | } 407 | } 408 | 409 | func loadPropItem() (props map[int32]*ItemProp) { 410 | props = make(map[int32]*ItemProp) 411 | r := &reader.Reader{Filename: "propItem.txt"} 412 | if err := r.ReadAll(); err != nil { 413 | panic(err) 414 | } 415 | 416 | lines, err := scanLines(r) 417 | if err != nil { 418 | panic(err.Error()) 419 | } 420 | 421 | for _, line := range lines { 422 | prop := new(ItemProp) 423 | for i, field := range strings.Fields(line) { 424 | fillField(prop, i, strings.TrimSpace(field), r) 425 | } 426 | props[prop.ID] = prop 427 | //props = append(props, prop) 428 | } 429 | 430 | return props 431 | } 432 | 433 | func scanLines(r *reader.Reader) ([]string, error) { 434 | scanner := bufio.NewScanner(r.BytesReader) 435 | 436 | scanner.Split(bufio.ScanLines) 437 | 438 | var lines []string 439 | for scanner.Scan() { 440 | line := strings.TrimSpace(scanner.Text()) 441 | if strings.HasPrefix(line, "//") { 442 | continue 443 | } 444 | if len(strings.Fields(line)) != 124 { 445 | continue 446 | } 447 | 448 | lines = append(lines, line) 449 | } 450 | 451 | return lines, nil 452 | } 453 | -------------------------------------------------------------------------------- /common/service/resources/reader/reader.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | ) 8 | 9 | // Reader is the main resource reader & loader for flyff's files 10 | type Reader struct { 11 | Filename string 12 | Bytes []byte 13 | BytesReader *bytes.Reader 14 | } 15 | 16 | // ReadAll file & return a buffer or error 17 | func (r *Reader) ReadAll() error { 18 | b, err := ioutil.ReadFile(fmt.Sprintf("resources/%s", r.Filename)) 19 | if err != nil { 20 | return err 21 | } 22 | 23 | r.BytesReader = bytes.NewReader(b) 24 | r.Bytes = b 25 | return err 26 | } 27 | -------------------------------------------------------------------------------- /common/service/resources/reader/types.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "go-ff/common/service/resources/defines" 5 | "strconv" 6 | ) 7 | 8 | // GetFloat64 helper method 9 | func (r *Reader) GetFloat64(word string) float64 { 10 | if word == "=" { 11 | return 0.0 12 | } 13 | 14 | num, _ := strconv.ParseFloat(word, 64) 15 | return num 16 | } 17 | 18 | // GetUInt64 helper method 19 | func (r *Reader) GetUInt64(word string) uint64 { 20 | if word == "=" { 21 | return 0 22 | } 23 | 24 | num, err := strconv.ParseUint(word, 10, 64) 25 | if err != nil { 26 | define, ok := defines.List[word] 27 | if ok { 28 | return (uint64)(define) 29 | } 30 | } 31 | 32 | return num 33 | } 34 | 35 | // GetUInt32 helper method 36 | func (r *Reader) GetUInt32(word string) uint32 { 37 | if word == "=" { 38 | return 0 39 | } 40 | 41 | num, err := strconv.ParseUint(word, 10, 32) 42 | if err != nil { 43 | define, ok := defines.List[word] 44 | if ok { 45 | return (uint32)(define) 46 | } 47 | } 48 | 49 | return (uint32)(num) 50 | } 51 | 52 | // GetUInt16 helper method 53 | func (r *Reader) GetUInt16(word string) uint16 { 54 | if word == "=" { 55 | return 0 56 | } 57 | 58 | num, err := strconv.ParseUint(word, 10, 16) 59 | if err != nil { 60 | define, ok := defines.List[word] 61 | if ok { 62 | return (uint16)(define) 63 | } 64 | } 65 | 66 | return (uint16)(num) 67 | } 68 | 69 | // GetUInt8 helper method 70 | func (r *Reader) GetUInt8(word string) uint8 { 71 | if word == "=" { 72 | return 0 73 | } 74 | 75 | num, err := strconv.ParseUint(word, 10, 8) 76 | if err != nil { 77 | define, ok := defines.List[word] 78 | if ok { 79 | return (uint8)(define) 80 | } 81 | } 82 | 83 | return (uint8)(num) 84 | } 85 | 86 | // GetInt64 helper method 87 | func (r *Reader) GetInt64(word string) int64 { 88 | if word == "=" { 89 | return 0 90 | } 91 | 92 | num, err := strconv.ParseInt(word, 10, 64) 93 | if err != nil { 94 | define, ok := defines.List[word] 95 | if ok { 96 | return define 97 | } 98 | } 99 | 100 | return num 101 | } 102 | 103 | // GetInt32 helper method 104 | func (r *Reader) GetInt32(word string) int32 { 105 | if word == "=" { 106 | return 0 107 | } 108 | 109 | num, err := strconv.ParseInt(word, 10, 32) 110 | if err != nil { 111 | define, ok := defines.List[word] 112 | if ok { 113 | return (int32)(define) 114 | } 115 | } 116 | 117 | return (int32)(num) 118 | } 119 | 120 | // GetInt16 helper method 121 | func (r *Reader) GetInt16(word string) int16 { 122 | if word == "=" { 123 | return 0 124 | } 125 | 126 | num, err := strconv.ParseInt(word, 10, 16) 127 | if err != nil { 128 | define, ok := defines.List[word] 129 | if ok { 130 | return (int16)(define) 131 | } 132 | } 133 | 134 | return (int16)(num) 135 | } 136 | 137 | // GetBool helper method 138 | // It also read 'TRUE' as true, 'FALSE' as false 139 | // n <= 0 as false, 0 < n as true 140 | func (r *Reader) GetBool(word string) bool { 141 | if word == "TRUE" || word == "true" { 142 | return true 143 | } 144 | if word == "FALSE" || word == "false" { 145 | return false 146 | } 147 | 148 | num, _ := strconv.ParseInt(word, 10, 16) 149 | return num > 0 150 | } 151 | -------------------------------------------------------------------------------- /common/service/timetick/timetick.go: -------------------------------------------------------------------------------- 1 | package timetick 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Cancellation cancels the channel 8 | type Cancellation bool 9 | 10 | // BeginTick in the given channel 11 | func BeginTick(done <-chan Cancellation, c chan<- int, t time.Duration) { 12 | prev := time.Now().Nanosecond() 13 | 14 | for { 15 | curr := time.Now().Nanosecond() 16 | gametime := (curr - prev) / 1000000 17 | prev = curr 18 | 19 | c <- gametime 20 | 21 | time.Sleep(t) 22 | 23 | select { 24 | case <-done: 25 | return 26 | default: 27 | break 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /config/.env: -------------------------------------------------------------------------------- 1 | AMQP_ENDPOINT="amqp://guest:guest@localhost:5672" 2 | MYSQL_CACHE_ENDPOINT="user:password@tcp(localhost:3306)/db?charset=utf8&parseTime=True&loc=Local" 3 | POSTGRES_DB_ENDPOINT="host=localhost port=5432 user=user dbname=db password=password sslmode=disable" 4 | 5 | # Message expiration in the Queue 6 | MESSAGE_EXPIRAITON=5000 7 | -------------------------------------------------------------------------------- /config/docker.env: -------------------------------------------------------------------------------- 1 | AMQP_ENDPOINT="amqp://guest:guest@broker:5672" 2 | MYSQL_CACHE_ENDPOINT="user:password@tcp(cache:3306)/db?charset=utf8&parseTime=True&loc=Local" 3 | POSTGRES_DB_ENDPOINT="host=db port=5432 user=user dbname=db password=password sslmode=disable" 4 | 5 | # Message expiration in the Queue 6 | MESSAGE_EXPIRAITON=5000 7 | -------------------------------------------------------------------------------- /config/modd/action.conf: -------------------------------------------------------------------------------- 1 | action/**/*.go common/**/*.go { 2 | daemon: go run action/main.go 3 | } -------------------------------------------------------------------------------- /connectionserver/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "go-ff/common/def/packet/packettype" 6 | "go-ff/common/service/dotenv" 7 | "go-ff/common/service/external" 8 | "go-ff/common/service/messaging" 9 | "go-ff/connectionserver/service/connectionmanager" 10 | "log" 11 | ) 12 | 13 | func onConnectedHandler(ch <-chan *external.Client) { 14 | for { 15 | c := <-ch 16 | if c == nil { 17 | continue 18 | } 19 | 20 | connectionmanager.Add(c) 21 | c.SendGreetings() 22 | } 23 | } 24 | 25 | func onDisconnectedHandler(ch <-chan *external.Client) { 26 | for { 27 | c := <-ch 28 | if c == nil { 29 | continue 30 | } 31 | 32 | connectionmanager.Remove(c) 33 | messaging.Publish(messaging.EntityTopic, &external.PacketHandler{ 34 | ClientID: c.ID, 35 | Packet: external.MakePacket(packettype.Disconnect).FinalizeForInternal(), 36 | }) 37 | } 38 | } 39 | 40 | func onMessageHandler(ch <-chan *external.PacketHandler) { 41 | for { 42 | p := <-ch 43 | if p == nil { 44 | continue 45 | } 46 | 47 | // Always FFFFFFF 48 | p.Packet.ReadUInt32() 49 | 50 | id := p.Packet.ReadUInt32() 51 | p.Packet.Offset -= (32 / 8) 52 | 53 | switch id { 54 | case 0xFFFFFF00: 55 | { 56 | p.Packet.Offset += (32 / 8) 57 | 58 | // Snapshot 59 | p.Packet.ReadUInt8() 60 | snapshotProtocol := p.Packet.ReadUInt16() 61 | p.Packet.Offset -= ((8 / 8) + (16 / 8) + (32 / 8)) 62 | if snapshotProtocol == 0x00C1 { 63 | messaging.Publish(messaging.MovingTopic, p) 64 | } 65 | } 66 | case 0xFFFFFF01, 0xFFFFFF02: 67 | { 68 | messaging.Publish(messaging.MovingTopic, p) 69 | } 70 | case 0xFF00: 71 | { 72 | messaging.Publish(messaging.EntityTopic, p) 73 | } 74 | case 0x00FF0000: 75 | { 76 | messaging.Publish(messaging.ChatTopic, p) 77 | } 78 | case packettype.Doequip, packettype.Moveitem, packettype.Dropitem: 79 | { 80 | messaging.Publish(messaging.ActionTopic, p) 81 | } 82 | default: 83 | { 84 | log.Printf("Unknown packet '0x%x'", id) 85 | } 86 | } 87 | } 88 | } 89 | 90 | func onInternalMessageHandler(ch <-chan []byte) { 91 | for { 92 | d := <-ch 93 | if d == nil { 94 | continue 95 | } 96 | 97 | var p external.PacketEmitter 98 | err := json.Unmarshal(d, &p) 99 | if err != nil { 100 | continue 101 | } 102 | 103 | p.Packet.Finalize() 104 | 105 | for _, id := range p.To { 106 | c := connectionmanager.Get(id) 107 | if c == nil { 108 | continue 109 | } 110 | 111 | c.SendFinalized(p.Packet) 112 | } 113 | } 114 | } 115 | 116 | func main() { 117 | // Initializers ---- 118 | log.SetFlags(log.LstdFlags | log.Lshortfile) 119 | dotenv.Initialize() 120 | messaging.Initialize() 121 | 122 | // Internal ---- 123 | onInternalMessage := make(chan []byte) 124 | go messaging.Subscribe(messaging.ConnectionTopic, onInternalMessage) 125 | go onInternalMessageHandler(onInternalMessage) 126 | 127 | // External ---- 128 | onConnected := make(chan *external.Client) 129 | onDisconnected := make(chan *external.Client) 130 | onMessage := make(chan *external.PacketHandler) 131 | 132 | go onConnectedHandler(onConnected) 133 | go onDisconnectedHandler(onDisconnected) 134 | go onMessageHandler(onMessage) 135 | 136 | server := external.Create("0.0.0.0:5400") 137 | server.OnConnected(onConnected) 138 | server.OnDisconnected(onDisconnected) 139 | server.OnMessage(onMessage) 140 | server.Start() 141 | } 142 | -------------------------------------------------------------------------------- /connectionserver/service/connectionmanager/connectionmanager.go: -------------------------------------------------------------------------------- 1 | package connectionmanager 2 | 3 | import ( 4 | "go-ff/common/service/external" 5 | "sync" 6 | ) 7 | 8 | var clients = make(map[uint32]*external.Client) 9 | var clientsMut sync.RWMutex 10 | 11 | // Add a new Client 12 | func Add(c *external.Client) bool { 13 | clientsMut.Lock() 14 | defer clientsMut.Unlock() 15 | 16 | // Exists ? 17 | _, ok := clients[c.ID] 18 | if ok == true { 19 | return false 20 | } 21 | 22 | clients[c.ID] = c 23 | return true 24 | } 25 | 26 | // Remove a Client 27 | func Remove(c *external.Client) { 28 | clientsMut.Lock() 29 | defer clientsMut.Unlock() 30 | 31 | delete(clients, c.ID) 32 | } 33 | 34 | // Get Client by ID 35 | func Get(id uint32) *external.Client { 36 | client, ok := clients[id] 37 | if ok == false { 38 | return nil 39 | } 40 | 41 | return client 42 | } 43 | -------------------------------------------------------------------------------- /deps.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1-alpine 2 | WORKDIR /go/src/go-ff 3 | 4 | RUN apk add git gcc g++ make dep 5 | RUN go get github.com/cortesi/modd/cmd/modd 6 | 7 | ADD . . 8 | 9 | RUN go get -v ./... 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | broker: 4 | image: rabbitmq:3-alpine 5 | restart: on-failure 6 | db: 7 | image: postgres:11-alpine 8 | environment: 9 | POSTGRES_PASSWORD: password 10 | POSTGRES_USER: user 11 | POSTGRES_DB: db 12 | ports: 13 | - 5432:5432 14 | restart: on-failure 15 | volumes: 16 | - db-data:/var/lib/mysql 17 | cache: 18 | image: mariadb:10.3 19 | environment: 20 | MYSQL_RANDOM_ROOT_PASSWORD: "yes" 21 | MYSQL_USER: user 22 | MYSQL_PASSWORD: password 23 | MYSQL_DATABASE: db 24 | ports: 25 | - 3306:3306 26 | restart: on-failure 27 | wait_dependencies: 28 | image: dadarek/wait-for-dependencies 29 | depends_on: 30 | - db 31 | - cache 32 | - broker 33 | command: db:5432 cache:3306 broker:5672 34 | login: 35 | image: flyff-deps 36 | build: 37 | context: . 38 | dockerfile: deps.Dockerfile 39 | depends_on: 40 | - wait_dependencies 41 | environment: 42 | ENV_FILE: docker.env 43 | entrypoint: go 44 | command: 45 | - "run" 46 | - "login/main.go" 47 | - "login/packet.go" 48 | - "login/servers.go" 49 | ports: 50 | - 23000:23000 51 | restart: on-failure 52 | volumes: 53 | - .:/go/src/go-ff/ 54 | cluster: 55 | image: flyff-deps 56 | build: 57 | context: . 58 | dockerfile: deps.Dockerfile 59 | depends_on: 60 | - wait_dependencies 61 | environment: 62 | ENV_FILE: docker.env 63 | entrypoint: go 64 | command: 65 | - "run" 66 | - "cluster/main.go" 67 | - "cluster/packets.go" 68 | ports: 69 | - 28000:28000 70 | restart: on-failure 71 | volumes: 72 | - .:/go/src/go-ff/ 73 | connection: 74 | image: flyff-deps 75 | build: 76 | context: . 77 | dockerfile: deps.Dockerfile 78 | depends_on: 79 | - wait_dependencies 80 | environment: 81 | ENV_FILE: docker.env 82 | entrypoint: go 83 | command: 84 | - "run" 85 | - "connectionserver/main.go" 86 | ports: 87 | - 5400:5400 88 | restart: on-failure 89 | volumes: 90 | - .:/go/src/go-ff/ 91 | entity: 92 | image: flyff-deps 93 | build: 94 | context: . 95 | dockerfile: deps.Dockerfile 96 | depends_on: 97 | - wait_dependencies 98 | environment: 99 | ENV_FILE: docker.env 100 | entrypoint: go 101 | command: 102 | - "run" 103 | - "entity/main.go" 104 | restart: on-failure 105 | volumes: 106 | - .:/go/src/go-ff/ 107 | chat: 108 | image: flyff-deps 109 | build: 110 | context: . 111 | dockerfile: deps.Dockerfile 112 | depends_on: 113 | - wait_dependencies 114 | environment: 115 | ENV_FILE: docker.env 116 | entrypoint: go 117 | command: 118 | - "run" 119 | - "chat/main.go" 120 | restart: on-failure 121 | volumes: 122 | - .:/go/src/go-ff/ 123 | moving: 124 | image: flyff-deps 125 | build: 126 | context: . 127 | dockerfile: deps.Dockerfile 128 | depends_on: 129 | - wait_dependencies 130 | environment: 131 | ENV_FILE: docker.env 132 | entrypoint: go 133 | command: 134 | - "run" 135 | - "moving/main.go" 136 | restart: on-failure 137 | volumes: 138 | - .:/go/src/go-ff/ 139 | action: 140 | image: flyff-deps 141 | build: 142 | context: . 143 | dockerfile: deps.Dockerfile 144 | depends_on: 145 | - wait_dependencies 146 | environment: 147 | ENV_FILE: docker.env 148 | entrypoint: modd 149 | command: 150 | - "-f" 151 | - "config/modd/action.conf" 152 | restart: on-failure 153 | volumes: 154 | - .:/go/src/go-ff/ 155 | volumes: 156 | db-data: 157 | -------------------------------------------------------------------------------- /entity/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "go-ff/common/def/packet/packettype" 6 | "go-ff/common/service/cache" 7 | "go-ff/common/service/database" 8 | "go-ff/common/service/dotenv" 9 | "go-ff/common/service/external" 10 | "go-ff/common/service/messaging" 11 | "go-ff/entity/packets/incoming" 12 | "log" 13 | ) 14 | 15 | func main() { 16 | log.SetFlags(log.LstdFlags | log.Lshortfile) 17 | dotenv.Initialize() 18 | database.Initialize() 19 | cache.Initialize() 20 | messaging.Initialize() 21 | 22 | ch := make(chan []byte) 23 | go messaging.Subscribe(messaging.EntityTopic, ch) 24 | 25 | for { 26 | b := <-ch 27 | p := new(external.PacketHandler) 28 | if err := json.Unmarshal(b, p); err != nil { 29 | log.Print(err) 30 | continue 31 | } 32 | 33 | id := p.Packet.ReadUInt32() 34 | switch id { 35 | case packettype.Join: 36 | { 37 | incoming.Join(p) 38 | break 39 | } 40 | case packettype.Disconnect: 41 | { 42 | incoming.Disconnect(p) 43 | break 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /entity/packets/incoming/disconnect.go: -------------------------------------------------------------------------------- 1 | package incoming 2 | 3 | import ( 4 | "go-ff/common/service/cache" 5 | "go-ff/common/service/database" 6 | "go-ff/common/service/external" 7 | "go-ff/common/service/messaging" 8 | "go-ff/entity/packets/outgoing" 9 | "log" 10 | ) 11 | 12 | // Disconnect from World 13 | func Disconnect(p *external.PacketHandler) { 14 | player := cache.FindByNetID(p.ClientID) 15 | if player == nil { 16 | return 17 | } 18 | 19 | cache.Connection.Where("net_client_id = ?", p.ClientID).Delete(player) 20 | 21 | var dbPlayer database.Player 22 | err := database.Connection.Model(&dbPlayer).Preload("Items").Where("id = ?", player.PlayerID).First(&dbPlayer).Error 23 | if err != nil { 24 | log.Print(err) 25 | return 26 | } 27 | 28 | // Clear previous inventory (Pretty bad yeah...) 29 | database.Connection.Model(&database.Item{}).Where("player_id = ?", player.PlayerID).Delete(database.Item{}) 30 | 31 | dbPlayer.FaceID = player.FaceID 32 | dbPlayer.Gender = player.Gender 33 | dbPlayer.HairColor = player.HairColor 34 | dbPlayer.Items = player.Inventory.ConvertToDatabaseSlice() 35 | dbPlayer.JobID = player.JobID 36 | dbPlayer.Level = player.Level 37 | dbPlayer.Name = player.Name 38 | dbPlayer.Position = player.Position 39 | dbPlayer.SkinSetID = player.SkinSetID 40 | dbPlayer.Statistics = player.Statistics 41 | 42 | database.Connection.Save(dbPlayer) 43 | 44 | // Make Visible ---- 45 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 46 | Packet: outgoing.DeleteObj(player), 47 | To: cache.FindIDAround(player), 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /entity/packets/incoming/join.go: -------------------------------------------------------------------------------- 1 | package incoming 2 | 3 | import ( 4 | "go-ff/common/service/cache" 5 | "go-ff/common/service/database" 6 | "go-ff/common/service/external" 7 | "go-ff/common/service/messaging" 8 | "go-ff/entity/packets/outgoing" 9 | "log" 10 | ) 11 | 12 | // JoinPacket struct 13 | type JoinPacket struct { 14 | WorldID uint32 15 | PlayerID uint32 16 | AuthenticationKey int32 17 | PartyID uint32 18 | GuildID uint32 19 | GuildWarID uint32 20 | IDOfMulti int32 21 | Slot uint8 22 | PlayerName string 23 | Username string 24 | Password string 25 | MessengerState int32 26 | MessengerCount int32 27 | } 28 | 29 | // Construct ... 30 | func (d *JoinPacket) Construct(p *external.Packet) { 31 | d.WorldID = p.ReadUInt32() 32 | d.PlayerID = p.ReadUInt32() 33 | d.AuthenticationKey = p.ReadInt32() 34 | d.PartyID = p.ReadUInt32() 35 | d.GuildID = p.ReadUInt32() 36 | d.GuildWarID = p.ReadUInt32() 37 | d.IDOfMulti = p.ReadInt32() 38 | d.Slot = p.ReadUInt8() 39 | d.PlayerName = p.ReadString() 40 | d.Username = p.ReadString() 41 | d.Password = p.ReadString() 42 | d.MessengerState = p.ReadInt32() 43 | d.MessengerCount = p.ReadInt32() 44 | } 45 | 46 | // Join World request 47 | func Join(p *external.PacketHandler) { 48 | var join JoinPacket 49 | join.Construct(p.Packet) 50 | 51 | db := database.Connection 52 | 53 | var player database.Player 54 | player.ID = join.PlayerID 55 | 56 | err := db.Preload("Items").First(&player).Error 57 | if err != nil { 58 | log.Print(err) 59 | return 60 | } 61 | 62 | entitiy := new(cache.Player) 63 | entitiy.NetClientID = p.ClientID 64 | entitiy.EntityID = external.GenerateID() 65 | entitiy.PlayerID = uint32(player.ID) 66 | entitiy.Slot = player.Slot 67 | entitiy.HairColor = player.HairColor 68 | entitiy.HairID = player.HairID 69 | entitiy.FaceID = player.FaceID 70 | entitiy.SkinSetID = player.SkinSetID 71 | entitiy.JobID = player.JobID 72 | entitiy.Angle = 360.0 73 | entitiy.Gender = player.Gender 74 | entitiy.Level = player.Level 75 | entitiy.Type = 5 // Mover 76 | entitiy.Size = 100 77 | entitiy.Position = player.Position 78 | entitiy.Name = player.Name 79 | if player.Gender == 0 { 80 | entitiy.ModelID = 11 81 | } else if player.Gender == 1 { 82 | entitiy.ModelID = 12 83 | } 84 | entitiy.Statistics = player.Statistics 85 | entitiy.Inventory = entitiy.Inventory.InitializeWith(player.Items) 86 | 87 | // Tx BEGIN ---- 88 | err = cache.Connection.Create(entitiy).Error 89 | if err != nil { 90 | log.Print(err) 91 | return 92 | } 93 | // Tx END ---- 94 | 95 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 96 | Packet: outgoing.Spawn(entitiy), 97 | To: []uint32{p.ClientID}, 98 | }) 99 | 100 | // Make others visible for him ---- 101 | players := cache.FindAroundOnly(entitiy) 102 | for _, aroundPlayer := range players { 103 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 104 | Packet: outgoing.AddObj(&aroundPlayer), 105 | To: []uint32{entitiy.NetClientID}, 106 | }) 107 | } 108 | 109 | toList := cache.FindIDAroundOnly(entitiy) 110 | if len(toList) < 1 { 111 | return 112 | } 113 | 114 | // Make Visible for Others ---- 115 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 116 | Packet: outgoing.AddObj(entitiy), 117 | To: toList, 118 | }) 119 | } 120 | -------------------------------------------------------------------------------- /entity/packets/outgoing/despawn.go: -------------------------------------------------------------------------------- 1 | package outgoing 2 | 3 | import ( 4 | "go-ff/common/def/packet/snapshottype" 5 | "go-ff/common/service/cache" 6 | "go-ff/common/service/external" 7 | ) 8 | 9 | // DeleteObj from the world 10 | func DeleteObj(p *cache.Player) *external.Packet { 11 | return external.StartMergePacket(p.EntityID, snapshottype.Del_obj, 0xFFFFFF00) 12 | } 13 | -------------------------------------------------------------------------------- /entity/packets/outgoing/spawn.go: -------------------------------------------------------------------------------- 1 | package outgoing 2 | 3 | import ( 4 | "go-ff/common/def/packet/snapshottype" 5 | "go-ff/common/feature/inventory" 6 | "go-ff/common/feature/inventory/def" 7 | "go-ff/common/service/cache" 8 | "go-ff/common/service/external" 9 | "math" 10 | 11 | . "github.com/ahmetb/go-linq" 12 | ) 13 | 14 | // Spawn packet 15 | func Spawn(pe *cache.Player) *external.Packet { 16 | p := external.StartMergePacket(pe.EntityID, snapshottype.Environmentall, 0x0000FF00). 17 | WriteUInt32(0) 18 | 19 | p.AddMergePart(snapshottype.World_readinfo, uint32(pe.EntityID)). 20 | WriteUInt32(pe.Position.MapID). 21 | WriteFloat32(float32(pe.Position.Vec.X)). 22 | WriteFloat32(float32(pe.Position.Vec.Y)). 23 | WriteFloat32(float32(pe.Position.Vec.Z)) 24 | 25 | p.AddMergePart(snapshottype.Add_obj, uint32(pe.EntityID)) 26 | 27 | p.WriteUInt8(5) 28 | if pe.Gender == 0 { 29 | p.WriteUInt32(11) 30 | } else if pe.Gender == 1 { 31 | p.WriteUInt32(12) 32 | } 33 | 34 | p.WriteUInt8(5) 35 | if pe.Gender == 0 { 36 | p.WriteUInt32(11) 37 | } else if pe.Gender == 1 { 38 | p.WriteUInt32(12) 39 | } 40 | 41 | p.WriteUInt16(100). 42 | WriteFloat32(float32(pe.Position.Vec.X)). 43 | WriteFloat32(float32(pe.Position.Vec.Y)). 44 | WriteFloat32(float32(pe.Position.Vec.Z)). 45 | WriteUInt16(360). 46 | WriteUInt32(uint32(pe.EntityID)) 47 | 48 | p.WriteUInt16(0). 49 | WriteUInt8(1). 50 | WriteUInt32(230). 51 | WriteUInt32(0). 52 | WriteUInt32(0). 53 | WriteUInt8(1). 54 | WriteInt32(-1) 55 | 56 | p.WriteString(pe.Name). 57 | WriteUInt8(pe.Gender). 58 | WriteUInt8(uint8(pe.SkinSetID)). 59 | WriteUInt8(uint8(pe.HairID)). 60 | WriteUInt32(pe.HairColor). 61 | WriteUInt8(uint8(pe.FaceID)). 62 | WriteUInt32(uint32(pe.PlayerID)). // Playerdata ID 63 | WriteUInt8(uint8(pe.JobID)). 64 | WriteUInt16(pe.Statistics.Strength). 65 | WriteUInt16(pe.Statistics.Stamina). 66 | WriteUInt16(pe.Statistics.Dexterity). 67 | WriteUInt16(pe.Statistics.Intelligence). 68 | WriteUInt16(uint16(pe.Level)) 69 | 70 | p.WriteInt32(-1). 71 | WriteUInt32(0). 72 | WriteUInt8(0). // Have guild or not 73 | WriteUInt32(0). 74 | WriteUInt8(0) // Have party 75 | 76 | // Auth 77 | p.WriteUInt8(100). 78 | WriteUInt32(0). 79 | WriteUInt32(0). 80 | WriteUInt32(0x000001F6). // item used ?? 81 | WriteUInt32(0). 82 | WriteUInt32(0). 83 | WriteUInt32(0). 84 | WriteUInt32(0). 85 | WriteUInt32(0). 86 | WriteUInt8(0). // duel 87 | WriteInt32(-1) // titles 88 | 89 | items := pe.Inventory[inventory.EquipOffset:] 90 | for i := 0; i < len(items); i++ { 91 | p.WriteUInt32(0) 92 | } 93 | 94 | p.WriteUInt32(0) 95 | 96 | for i := 0; i < 26; i++ { 97 | p.WriteUInt32(0) 98 | } 99 | 100 | p.WriteUInt16(0). 101 | WriteUInt16(0). 102 | WriteUInt32(0). 103 | WriteUInt32(0). 104 | WriteUInt32(0). // Gold 105 | WriteUInt64(0). // exp 106 | WriteUInt32(0). 107 | WriteUInt32(0). 108 | WriteUInt64(0). // Death exp 109 | WriteUInt32(0) 110 | 111 | for i := 0; i < 32; i++ { 112 | p.WriteUInt32(0) 113 | } 114 | 115 | // Marking pos 116 | p.WriteUInt32(pe.Position.MapID). 117 | WriteFloat32(float32(pe.Position.Vec.X)). 118 | WriteFloat32(float32(pe.Position.Vec.Y)). 119 | WriteFloat32(float32(pe.Position.Vec.Z)) 120 | 121 | p.WriteUInt8(0). 122 | WriteUInt8(0). 123 | WriteUInt8(0) 124 | 125 | p.WriteUInt32(42). 126 | WriteUInt16(0). // Stats points 127 | WriteUInt16(0) 128 | 129 | for _, i := range items { 130 | p.WriteInt32(i.ItemID) 131 | } 132 | 133 | for i := 0; i < 45; i++ { 134 | p.WriteInt32(-1). 135 | WriteUInt32(0) 136 | } 137 | 138 | p.WriteUInt8(0). // Cheer point 139 | WriteUInt32(0) 140 | 141 | p.WriteUInt8(pe.Slot) 142 | for i := 0; i < 3; i++ { 143 | p.WriteUInt32(0). 144 | WriteUInt32(0) 145 | } 146 | 147 | p.WriteUInt32(1). 148 | WriteUInt8(0). 149 | WriteUInt8(0). 150 | WriteUInt8(0). 151 | WriteUInt64(0). 152 | WriteUInt32(0) 153 | 154 | pe.Inventory.Serialize(p) 155 | 156 | for i := 0; i < 3; i++ { 157 | for j := 0; j < 42; j++ { 158 | p.WriteUInt32(uint32(j)) 159 | } 160 | p.WriteUInt8(0) 161 | for j := 0; j < 42; j++ { 162 | p.WriteUInt32(uint32(j)) 163 | } 164 | } 165 | 166 | p.WriteInt32(math.MaxInt32) 167 | 168 | // Bag 169 | p.WriteInt8(1) 170 | for i := 0; i < 6; i++ { 171 | p.WriteUInt32(uint32(i)) 172 | } 173 | p.WriteInt8(0) 174 | for i := 0; i < 6; i++ { 175 | p.WriteUInt32(uint32(i)) 176 | } 177 | 178 | p.WriteUInt32(1). 179 | WriteUInt32(1). 180 | WriteInt8(0). 181 | WriteUInt32(1) 182 | 183 | for i := 0; i < 150; i++ { 184 | p.WriteUInt32(0) 185 | } 186 | 187 | p.WriteUInt32(0). 188 | WriteUInt32(0). 189 | WriteUInt32(0) 190 | 191 | return p 192 | } 193 | 194 | // AddObj packet (make visible this object to others) 195 | func AddObj(p *cache.Player) *external.Packet { 196 | packet := external.StartMergePacket(p.EntityID, snapshottype.Add_obj, 0xFFFFFF00) 197 | packet.WriteUInt8(5) 198 | if p.Gender == 0 { 199 | packet.WriteUInt32(11) 200 | } else if p.Gender == 1 { 201 | packet.WriteUInt32(12) 202 | } 203 | packet.WriteUInt8(5) 204 | if p.Gender == 0 { 205 | packet.WriteUInt32(11) 206 | } else if p.Gender == 1 { 207 | packet.WriteUInt32(12) 208 | } 209 | packet.WriteUInt16(100). 210 | WriteFloat32(float32(p.Position.Vec.X)). 211 | WriteFloat32(float32(p.Position.Vec.Y)). 212 | WriteFloat32(float32(p.Position.Vec.Z)). 213 | WriteUInt16(360). 214 | WriteUInt32(uint32(p.EntityID)). 215 | WriteUInt16(0). 216 | WriteUInt8(1). 217 | WriteUInt32(230). 218 | WriteUInt32(0). 219 | WriteUInt32(0). 220 | WriteUInt8(1). 221 | WriteInt32(-1). 222 | WriteString(p.Name). 223 | WriteUInt8(p.Gender). 224 | WriteUInt8(uint8(p.SkinSetID)). 225 | WriteUInt8(uint8(p.HairID)). 226 | WriteUInt32(p.HairColor). 227 | WriteUInt8(uint8(p.FaceID)). 228 | WriteUInt32(uint32(p.EntityID)). 229 | WriteUInt8(p.JobID). 230 | WriteUInt16(p.Statistics.Strength). 231 | WriteUInt16(p.Statistics.Stamina). 232 | WriteUInt16(p.Statistics.Dexterity). 233 | WriteUInt16(p.Statistics.Intelligence). 234 | WriteUInt16(uint16(p.Level)). 235 | WriteInt32(-1). 236 | WriteUInt32(0). 237 | WriteUInt8(0). 238 | WriteUInt32(0). 239 | WriteUInt8(0). 240 | WriteUInt8(100). 241 | WriteUInt32(0). 242 | WriteUInt32(0). 243 | WriteUInt32(0). 244 | WriteUInt32(0). 245 | WriteUInt32(0). 246 | WriteUInt32(0). 247 | WriteUInt32(0). 248 | WriteUInt32(0). 249 | WriteUInt8(0). 250 | WriteInt32(-1) 251 | 252 | items := p.Inventory[inventory.EquipOffset:] 253 | for i := 0; i < len(items); i++ { 254 | packet.WriteInt32(0) 255 | } 256 | 257 | for i := 0; i < 28; i++ { 258 | packet.WriteUInt32(0) 259 | } 260 | 261 | var equipedItems []def.Item 262 | From(items). 263 | WhereT(func(item def.Item) bool { 264 | return item.ItemID > 0 265 | }). 266 | SelectT(func(item def.Item) def.Item { 267 | return item 268 | }). 269 | ToSlice(&equipedItems) 270 | 271 | packet.WriteUInt8(uint8(len(equipedItems))) 272 | for _, item := range equipedItems { 273 | packet.WriteUInt8(uint8(item.Position - inventory.EquipOffset)). 274 | WriteUInt16(uint16(item.ItemID)). 275 | WriteUInt8(0) 276 | } 277 | 278 | packet.WriteInt32(-1). 279 | WriteUInt32(0) 280 | 281 | return packet 282 | } 283 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go-ff 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/ahmetb/go-linq v3.0.0+incompatible 7 | github.com/golang/geo v0.0.0-20190507233405-a0e886e97a51 8 | github.com/jinzhu/gorm v1.9.7 9 | github.com/joho/godotenv v1.3.0 10 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= 4 | cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= 5 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 7 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 8 | github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA= 9 | github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY= 10 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 11 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 12 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 13 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 14 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 15 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 16 | github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk= 17 | github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= 18 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 19 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 20 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 21 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 22 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 23 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 24 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 25 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 26 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 27 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 28 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 29 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 30 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 31 | github.com/golang/geo v0.0.0-20190507233405-a0e886e97a51 h1:MQn73MfXCNoQbk2UxlMcU7HMSiOipZ9KL97Lx+/5e/k= 32 | github.com/golang/geo v0.0.0-20190507233405-a0e886e97a51/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= 33 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 34 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 35 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 36 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 37 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 38 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 39 | github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= 40 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 41 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 42 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 43 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 44 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 45 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 46 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 47 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 48 | github.com/jinzhu/gorm v1.9.7 h1:W8/F2N9x/tq3Vt+S4W12WjCVZB11tyerJ8RPKpQ8Xts= 49 | github.com/jinzhu/gorm v1.9.7/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84= 50 | github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= 51 | github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 52 | github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns= 53 | github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= 54 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 55 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 56 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 57 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 58 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 59 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 60 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 61 | github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4= 62 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 63 | github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= 64 | github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 65 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 66 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 67 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 68 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 69 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 70 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 71 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 72 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 73 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 74 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 75 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 76 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 77 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 78 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 79 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 80 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 81 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 82 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 83 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 h1:0ngsPmuP6XIjiFRNFYlvKwSr5zff2v+uPHaffZ6/M4k= 84 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 85 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 86 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 87 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 88 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 89 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 90 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= 91 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 92 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 93 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 94 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 95 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 96 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 97 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 98 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 99 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 100 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 101 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 102 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 103 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 104 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 105 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 106 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 107 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 108 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 109 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 110 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 111 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 112 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 113 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 114 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 115 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 116 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 117 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 118 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 119 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 120 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 121 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 122 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 123 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 124 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 125 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 126 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 127 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 128 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 129 | google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 130 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 131 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 132 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 133 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 134 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 135 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 136 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 137 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 138 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 139 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 140 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 141 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 142 | -------------------------------------------------------------------------------- /login/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-ff/common/def/packet/packettype" 5 | "go-ff/common/service/dotenv" 6 | "go-ff/common/service/external" 7 | "go-ff/connectionserver/service/connectionmanager" 8 | "log" 9 | ) 10 | 11 | func onConnectedHandler(ch <-chan *external.Client) { 12 | for { 13 | c := <-ch 14 | if c == nil { 15 | continue 16 | } 17 | 18 | connectionmanager.Add(c) 19 | c.SendGreetings() 20 | } 21 | } 22 | 23 | func onDisconnectedHandler(ch <-chan *external.Client) { 24 | for { 25 | c := <-ch 26 | if c == nil { 27 | continue 28 | } 29 | 30 | connectionmanager.Remove(c) 31 | } 32 | } 33 | 34 | func onMessageHandler(ch <-chan *external.PacketHandler) { 35 | for { 36 | p := <-ch 37 | if p == nil { 38 | continue 39 | } 40 | 41 | c := connectionmanager.Get(p.ClientID) 42 | if c == nil { 43 | continue 44 | } 45 | 46 | id := p.Packet.ReadUInt32() 47 | if id == packettype.Certify { 48 | sendServerList(c) 49 | } 50 | } 51 | } 52 | 53 | func main() { 54 | log.SetFlags(log.LstdFlags | log.Lshortfile) 55 | dotenv.Initialize() 56 | 57 | // External ---- 58 | onConnected := make(chan *external.Client) 59 | onDisconnected := make(chan *external.Client) 60 | onMessage := make(chan *external.PacketHandler) 61 | 62 | go onConnectedHandler(onConnected) 63 | go onDisconnectedHandler(onDisconnected) 64 | go onMessageHandler(onMessage) 65 | 66 | server := external.Create("0.0.0.0:23000") 67 | server.OnConnected(onConnected) 68 | server.OnDisconnected(onDisconnected) 69 | server.OnMessage(onMessage) 70 | server.Start() 71 | } 72 | -------------------------------------------------------------------------------- /login/packet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-ff/common/def/packet/packettype" 5 | "math" 6 | 7 | "go-ff/common/service/external" 8 | ) 9 | 10 | func sendServerList(c *external.Client) { 11 | p := external.MakePacket(packettype.Srvr_list). 12 | WriteUInt32(0). 13 | WriteUInt8(1). 14 | WriteString("test"). 15 | WriteUInt32(2) 16 | 17 | for i, server := range servers { 18 | p.WriteUInt32(math.MaxUint32). 19 | WriteInt32(int32(i + 1)). 20 | WriteString(server.name). 21 | WriteString(server.ip). 22 | WriteUInt32(0). 23 | WriteUInt32(0). 24 | WriteUInt32(1). 25 | WriteUInt32(0) 26 | 27 | for j, channel := range server.channels { 28 | p.WriteUInt32(uint32(i + 1)). 29 | WriteUInt32(uint32(j + 1)). 30 | WriteString(channel.name). 31 | WriteString(channel.ip). 32 | WriteUInt32(0). 33 | WriteUInt32(0). 34 | WriteUInt32(1). 35 | WriteUInt32(channel.maxPlayer) 36 | } 37 | } 38 | 39 | c.Send(p) 40 | } 41 | -------------------------------------------------------------------------------- /login/servers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type channel struct { 4 | name string 5 | ip string 6 | maxPlayer uint32 7 | } 8 | 9 | type server struct { 10 | name string 11 | ip string 12 | channels []channel 13 | } 14 | 15 | var servers = []server{ 16 | server{ 17 | "Server 1", 18 | "192.168.1.38", 19 | []channel{ 20 | channel{ 21 | "Channel 1", 22 | "192.168.1.38", 23 | 500, 24 | }, 25 | }, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /moving/def/packets/behaviour.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "go-ff/common/service/external" 5 | 6 | "github.com/golang/geo/r3" 7 | ) 8 | 9 | // Behaviour packet struct 10 | type Behaviour struct { 11 | V *r3.Vector 12 | Vd *r3.Vector 13 | Angle float32 14 | State uint32 15 | StateFlag uint32 16 | Motion uint32 17 | MotionEx int32 18 | Loop int32 19 | MotionOptions uint32 20 | TickCount int64 21 | } 22 | 23 | // Construct from Packet 24 | func (b *Behaviour) Construct(p *external.Packet) { 25 | b.V = p.Read3DVector() 26 | b.Vd = p.Read3DVector() 27 | b.Angle = p.ReadFloat32() 28 | b.State = p.ReadUInt32() 29 | b.StateFlag = p.ReadUInt32() 30 | b.Motion = p.ReadUInt32() 31 | b.MotionEx = p.ReadInt32() 32 | b.Loop = p.ReadInt32() 33 | b.MotionOptions = p.ReadUInt32() 34 | b.TickCount = p.ReadInt64() 35 | } 36 | -------------------------------------------------------------------------------- /moving/feature/move/destpos.go: -------------------------------------------------------------------------------- 1 | package move 2 | 3 | import ( 4 | "go-ff/common/service/cache" 5 | "go-ff/common/service/timetick" 6 | "math" 7 | "time" 8 | 9 | "github.com/golang/geo/r3" 10 | ) 11 | 12 | func movePlayerByDest(p *cache.Player, t int) { 13 | if t < 0 { 14 | return 15 | } 16 | 17 | if p.Moving.Destination.Distance(p.Position.Vec) < 0.1 { 18 | p.Moving.Destination = r3.Vector{} 19 | p.Moving.Motion = 0 20 | SaveMovingComponent(p) 21 | return 22 | } 23 | 24 | var speed = (0.08 * 100.0) * (float64(t) / 1000.0) 25 | distX := p.Moving.Destination.X - p.Position.Vec.X 26 | distZ := p.Moving.Destination.Z - p.Position.Vec.Z 27 | distance := math.Sqrt(distX*distX + distZ*distZ) 28 | 29 | deltaX := distX / distance 30 | deltaZ := distZ / distance 31 | offsetX := deltaX * speed 32 | offsetZ := deltaZ * speed 33 | 34 | if math.Abs(offsetX) > math.Abs(distX) { 35 | offsetX = distX 36 | } 37 | if math.Abs(offsetZ) > math.Abs(distZ) { 38 | offsetZ = distZ 39 | } 40 | 41 | p.Position.Vec.X += offsetX 42 | p.Position.Vec.Z += offsetZ 43 | 44 | SavePosition(p) 45 | } 46 | 47 | // ProcessDestPosMove for the given (NetClientID) player 48 | func ProcessDestPosMove(id uint32, destPos r3.Vector) { 49 | done := make(chan timetick.Cancellation) 50 | tick := make(chan int) 51 | go timetick.BeginTick(done, tick, 100*time.Millisecond) 52 | 53 | for { 54 | t := <-tick 55 | p := cache.FindByNetID(id) 56 | if p == nil { 57 | return 58 | } 59 | 60 | if p.Moving.Destination != destPos { 61 | done <- true 62 | return 63 | } 64 | 65 | movePlayerByDest(p, t) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /moving/feature/move/move.go: -------------------------------------------------------------------------------- 1 | package move 2 | 3 | import ( 4 | "go-ff/common/service/cache" 5 | "go-ff/common/service/timetick" 6 | "math" 7 | "time" 8 | ) 9 | 10 | func movePlayer(p *cache.Player, t int, angle float64) { 11 | if t < 0 { 12 | return 13 | } 14 | 15 | theta := angle * math.Pi / 180 16 | 17 | var distance = (0.08 * 100.0) * (float64(t) / 1000.0) 18 | 19 | p.Position.Vec.X += (math.Sin(theta) * distance) 20 | p.Position.Vec.Z -= (math.Cos(theta) * distance) 21 | 22 | SavePosition(p) 23 | } 24 | 25 | // ProcessMove for the given (NetClientID) player 26 | func ProcessMove(id uint32, angle float64) { 27 | done := make(chan timetick.Cancellation) 28 | tick := make(chan int) 29 | go timetick.BeginTick(done, tick, 150*time.Millisecond) 30 | 31 | for { 32 | t := <-tick 33 | p := cache.FindByNetID(id) 34 | if p == nil { 35 | return 36 | } 37 | 38 | if p.Moving.Motion != 5 || p.Moving.Angle != angle { 39 | return 40 | } 41 | 42 | movePlayer(p, t, angle) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /moving/feature/move/save.go: -------------------------------------------------------------------------------- 1 | package move 2 | 3 | import ( 4 | "go-ff/common/service/cache" 5 | ) 6 | 7 | // SaveMovingComponent saves only movin_ fields 8 | func SaveMovingComponent(p *cache.Player) { 9 | cache.Connection.Model(p).Where("net_client_id = ?", p.NetClientID).Updates(map[string]interface{}{ 10 | "movin_x": p.Moving.Destination.X, 11 | "movin_y": p.Moving.Destination.Y, 12 | "movin_z": p.Moving.Destination.Z, 13 | "movin_motion": p.Moving.Motion, 14 | "movin_angle": p.Moving.Angle, 15 | }) 16 | } 17 | 18 | // SavePosition saves only posit_ fields 19 | func SavePosition(p *cache.Player) { 20 | cache.Connection.Model(p).Where("net_client_id = ?", p.NetClientID).Updates(map[string]interface{}{ 21 | "posit_x": p.Position.Vec.X, 22 | "posit_y": p.Position.Vec.Y, 23 | "posit_z": p.Position.Vec.Z, 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /moving/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "go-ff/common/service/cache" 6 | "go-ff/common/service/dotenv" 7 | "go-ff/common/service/external" 8 | "go-ff/common/service/messaging" 9 | "go-ff/moving/packets/incoming" 10 | "log" 11 | ) 12 | 13 | func main() { 14 | log.SetFlags(log.LstdFlags | log.Lshortfile) 15 | dotenv.Initialize() 16 | cache.Initialize() 17 | messaging.Initialize() 18 | 19 | ch := make(chan []byte) 20 | go messaging.Subscribe(messaging.MovingTopic, ch) 21 | 22 | for { 23 | b := <-ch 24 | p := new(external.PacketHandler) 25 | if err := json.Unmarshal(b, p); err != nil { 26 | log.Print(err) 27 | continue 28 | } 29 | 30 | id := p.Packet.ReadUInt32() 31 | switch id { 32 | case 0xffffff00: 33 | { 34 | p.Packet.ReadUInt8() 35 | snapshotProtocol := p.Packet.ReadUInt16() 36 | if snapshotProtocol == 0x00C1 { 37 | incoming.DestPos(p) 38 | } 39 | } 40 | case 0xFFFFFF01: 41 | { 42 | incoming.Move(p) 43 | } 44 | case 0xffffff02: 45 | { 46 | incoming.Behaviour(p) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /moving/packets/incoming/behaviour.go: -------------------------------------------------------------------------------- 1 | package incoming 2 | 3 | import ( 4 | "go-ff/common/service/cache" 5 | "go-ff/common/service/external" 6 | "go-ff/common/service/messaging" 7 | "go-ff/moving/def/packets" 8 | "go-ff/moving/feature/move" 9 | "go-ff/moving/packets/outgoing" 10 | ) 11 | 12 | // Behaviour packet handler 13 | func Behaviour(p *external.PacketHandler) { 14 | player := cache.FindByNetID(p.ClientID) 15 | if player == nil { 16 | return 17 | } 18 | 19 | behaviourPacket := new(packets.Behaviour) 20 | behaviourPacket.Construct(p.Packet) 21 | 22 | clientDestPos := behaviourPacket.V.Add(*behaviourPacket.Vd) 23 | serverDestPos := player.Position.Vec.Add(*behaviourPacket.Vd) 24 | distance := clientDestPos.Distance(serverDestPos) 25 | if distance >= 3.0 && distance <= 15.0 { 26 | go move.ProcessDestPosMove(player.NetClientID, clientDestPos) 27 | } 28 | 29 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 30 | Packet: outgoing.Behaviour(player, behaviourPacket).Finalize(), 31 | To: cache.FindIDAroundOnly(player), 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /moving/packets/incoming/move.go: -------------------------------------------------------------------------------- 1 | package incoming 2 | 3 | import ( 4 | "go-ff/common/service/cache" 5 | "go-ff/common/service/external" 6 | "go-ff/common/service/messaging" 7 | "go-ff/moving/def/packets" 8 | "go-ff/moving/feature/move" 9 | "go-ff/moving/packets/outgoing" 10 | 11 | "github.com/golang/geo/r3" 12 | ) 13 | 14 | // DestPos packet from player 15 | func DestPos(p *external.PacketHandler) { 16 | player := cache.FindByNetID(p.ClientID) 17 | if player == nil { 18 | return 19 | } 20 | 21 | player.Moving.Destination = *p.Packet.Read3DVector() 22 | player.Moving.Motion = 0 23 | move.SaveMovingComponent(player) 24 | 25 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 26 | Packet: outgoing.DestPos(player).Finalize(), 27 | To: cache.FindIDAround(player), 28 | }) 29 | 30 | go move.ProcessDestPosMove(player.NetClientID, player.Moving.Destination) 31 | } 32 | 33 | // Move by key handler 34 | func Move(p *external.PacketHandler) { 35 | player := cache.FindByNetID(p.ClientID) 36 | if player == nil { 37 | return 38 | } 39 | 40 | behaviourPacket := new(packets.Behaviour) 41 | behaviourPacket.Construct(p.Packet) 42 | 43 | player.Moving.Destination = r3.Vector{} 44 | player.Moving.Motion = behaviourPacket.Motion 45 | player.Moving.Angle = float64(behaviourPacket.Angle) 46 | move.SaveMovingComponent(player) 47 | 48 | messaging.Publish(messaging.ConnectionTopic, &external.PacketEmitter{ 49 | Packet: outgoing.Move(player, behaviourPacket).Finalize(), 50 | To: cache.FindIDAroundOnly(player), 51 | }) 52 | 53 | if behaviourPacket.Motion == 5 { 54 | go move.ProcessMove(p.ClientID, player.Moving.Angle) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /moving/packets/outgoing/behaviour.go: -------------------------------------------------------------------------------- 1 | package outgoing 2 | 3 | import ( 4 | "go-ff/common/def/packet/snapshottype" 5 | "go-ff/common/service/cache" 6 | "go-ff/common/service/external" 7 | "go-ff/moving/def/packets" 8 | ) 9 | 10 | // Behaviour packet emitter 11 | func Behaviour(p *cache.Player, b *packets.Behaviour) *external.Packet { 12 | return external.StartMergePacket(p.EntityID, snapshottype.Moverbehavior, 0x0000FF00). 13 | Write3DVector(b.V). 14 | Write3DVector(b.Vd). 15 | WriteFloat32(b.Angle). 16 | WriteUInt32(b.State). 17 | WriteUInt32(b.StateFlag). 18 | WriteUInt32(b.Motion). 19 | WriteInt32(b.MotionEx). 20 | WriteInt32(b.Loop). 21 | WriteUInt32(b.MotionOptions). 22 | WriteInt64(b.TickCount) 23 | } 24 | -------------------------------------------------------------------------------- /moving/packets/outgoing/move.go: -------------------------------------------------------------------------------- 1 | package outgoing 2 | 3 | import ( 4 | "go-ff/common/def/packet/snapshottype" 5 | "go-ff/common/service/cache" 6 | "go-ff/common/service/external" 7 | "go-ff/moving/def/packets" 8 | ) 9 | 10 | // DestPos packet 11 | func DestPos(p *cache.Player) *external.Packet { 12 | packet := external.StartMergePacket(p.EntityID, snapshottype.Destpos, 0x0000FF00). 13 | WriteFloat32(float32(p.Moving.Destination.X)). 14 | WriteFloat32(float32(p.Moving.Destination.Y)). 15 | WriteFloat32(float32(p.Moving.Destination.Z)). 16 | WriteUInt8(1) 17 | 18 | return packet.Finalize() 19 | } 20 | 21 | // Move packet emitter 22 | func Move(p *cache.Player, b *packets.Behaviour) *external.Packet { 23 | return external.StartMergePacket(p.EntityID, snapshottype.Movermoved, 0x0000FF00). 24 | Write3DVector(b.V). 25 | Write3DVector(b.Vd). 26 | WriteFloat32(b.Angle). 27 | WriteUInt32(b.State). 28 | WriteUInt32(b.StateFlag). 29 | WriteUInt32(b.Motion). 30 | WriteInt32(b.MotionEx). 31 | WriteInt32(b.Loop). 32 | WriteUInt32(b.MotionOptions). 33 | WriteInt64(b.TickCount) 34 | } 35 | -------------------------------------------------------------------------------- /resources/define.json: -------------------------------------------------------------------------------- 1 | { 2 | "FALSE": 0, 3 | "TRUE": 1, 4 | "WUI_NONE": 0, 5 | "WUI_NOW": 1, 6 | "WUI_TARGETOBJ": 2, 7 | "WUI_TARGETOBJ2": 3, 8 | "WUI_TARGETINGOBJ": 4, 9 | "WUI_TARGETOBJPTZ": 5, 10 | "WUI_TARGETPTZ": 6, 11 | "WUI_TARGETMOVEOBJ": 7, 12 | "WUI_TARGETMOVEPTZ": 8, 13 | "WUI_MENU": 9, 14 | "WUI_MENU_TOBJ": 10, 15 | "WUI_TARGETPTZ_IB": 11, 16 | "WUI_TARGETCURSORPTZ": 12, 17 | "WET_NONE": 0, 18 | "WET_NOW": 1, 19 | "WET_DEAD": 2, 20 | "WET_ATK": 3, 21 | "WET_ATKOTHER": 4, 22 | "WET_BODYSTATE": 5, 23 | "WET_PARRY": 6, 24 | "WET_ARROW": 7, 25 | "WET_BULLET": 8, 26 | "WET_RANGE": 9, 27 | "WET_MAGIC": 10, 28 | "WET_HIT": 11, 29 | "EXT_NONE": 0, 30 | "EXT_SELFCHGPARAMET": 1, 31 | "EXT_OBJCHGPARAMET": 2, 32 | "EXT_MAGICSHOT": 3, 33 | "EXT_MAGICATK": 4, 34 | "EXT_AMPLIFICATION": 5, 35 | "EXT_ATTACKER": 6, 36 | "EXT_MAGIC": 7, 37 | "EXT_ANOTHER": 8, 38 | "EXT_ANOTHERWITH": 9, 39 | "EXT_SUMMON": 10, 40 | "EXT_AROUNDATK": 11, 41 | "EXT_OTHER": 12, 42 | "EXT_TROUPE": 13, 43 | "EXT_MAGICATKSHOT": 14, 44 | "EXT_MENTALATK": 15, 45 | "EXT_MELEEATKSHOT": 16, 46 | "EXT_MELEEATK": 17, 47 | "EXT_RANGEATK": 18, 48 | "EXT_PET": 19, 49 | "EXT_TROUPEWITH": 20, 50 | "EXT_ITEM": 21, 51 | "WES_NONE": 0, 52 | "WES_WORLD": 1, 53 | "WES_DEATH": 2, 54 | "WES_DAMAGE": 3, 55 | "WES_EXECUTE": 4, 56 | "WES_DIALOG": 5, 57 | "WES_EVENT": 6, 58 | "WES_SCHEDULE_BEGIN": 7, 59 | "WES_SCHEDULE_END": 8, 60 | "SEX_MALE": 0, 61 | "SEX_FEMALE": 1, 62 | "SEX_SEXLESS": 2, 63 | "OT_OBJ": 0, 64 | "OT_ANI": 1, 65 | "OT_CTRL": 2, 66 | "OT_SFX": 3, 67 | "OT_ITEM": 4, 68 | "OT_MOVER": 5, 69 | "OT_REGION": 6, 70 | "OT_SHIP": 7, 71 | "OT_PATH": 8, 72 | "MAX_OBJTYPE": 9, 73 | "OF_OBJ": 0, 74 | "OF_ANI": 0, 75 | "OF_CTRL": 0, 76 | "OF_SFX": 0, 77 | "OF_ITEM": 0, 78 | "OF_MOVER": 0, 79 | "OF_REGION": 0, 80 | "OF_SHIP": 0, 81 | "MODELTYPE_NONE": 0, 82 | "MODELTYPE_MESH": 1, 83 | "MODELTYPE_ANIMATED_MESH": 2, 84 | "MODELTYPE_BILLBOARD": 3, 85 | "MODELTYPE_SFX": 4, 86 | "MODELTYPE_ASE": 5, 87 | "MD_FAR": 0, 88 | "MD_MID": 1, 89 | "MD_NEAR": 2, 90 | "MD_FIX": 3, 91 | "ATEX_NONE": 0, 92 | "ATEX_00": 0, 93 | "ATEX_USE": 1, 94 | "ATEX_01": 1, 95 | "ATEX_02": 2, 96 | "ATEX_03": 3, 97 | "ATEX_04": 4, 98 | "ATEX_05": 5, 99 | "ATEX_06": 6, 100 | "ATEX_07": 7, 101 | "ITYPE_ITEM": 0, 102 | "ITYPE_CARD": 1, 103 | "ITYPE_CUBE": 2, 104 | "ITYPE_PET": 3, 105 | "RA_WORLD": 0, 106 | "RA_DUNGEON": 0, 107 | "RA_NEWBIE": 0, 108 | "RA_BEGIN": 0, 109 | "RA_SAFETY": 0, 110 | "RA_SHRINE": 0, 111 | "RA_FIGHT": 0, 112 | "RA_INN": 0, 113 | "RA_SIGHT": 0, 114 | "RA_DAMAGE": 0, 115 | "RA_TOWN": 0, 116 | "RA_DAYLIGHT": 0, 117 | "RA_PK": 0, 118 | "RA_OX": 0, 119 | "RA_DANGER": 0, 120 | "RA_NO_CHAT": 0, 121 | "RA_NO_ATTACK": 0, 122 | "RA_NO_DAMAGE": 0, 123 | "RA_NO_SKILL": 0, 124 | "RA_NO_ITEM": 0, 125 | "RA_NO_TELEPORT": 0, 126 | "RA_SCHOOL": 0, 127 | "RA_PENALTY_PK": 0, 128 | "RA_COLLECTING": 0, 129 | "CUSTOM_LOGO_MAX": 27, 130 | "GM_LOGO_CH": 21, 131 | "GM_LOGO_EU": 22, 132 | "GM_LOGO_JP": 23, 133 | "GM_LOGO_PH": 24, 134 | "GM_LOGO_TH": 25, 135 | "GM_LOGO_TW": 26, 136 | "GM_LOGO_US": 27, 137 | "CITYN_FLARIS": 1, 138 | "CITYN_SAINTMORNING": 2, 139 | "LANDN_FLARIS": 1, 140 | "LANDN_SAINTMORNING": 2, 141 | "GUIDE_EVENT_MOVE": 0, 142 | "GUIDE_EVENT_KEY_MOVE": 1, 143 | "GUIDE_EVENT_KEY_RUN": 2, 144 | "GUIDE_EVENT_KEY_JUMP": 3, 145 | "GUIDE_EVENT_TRACKING_MOVE": 4, 146 | "GUIDE_EVENT_KEY_ZOOM": 5, 147 | "GUIDE_EVENT_CAMERAMOVE": 6, 148 | "GUIDE_EVENT_KEY_CAMERAMOVE": 7, 149 | "GUIDE_EVENT_INTRO": 8, 150 | "GUIDE_EVENT_END": 9, 151 | "GUIDE_EVENT_BERSERKERMODE": 10, 152 | "GUIDE_FLIGHT": 11, 153 | "GUIDE_FLIGHT_METHOD": 12, 154 | "APP_SKILL_BEFOREJOB": 13, 155 | "APP_SKILL_AFTERJOB": 14, 156 | "APP_DEATH_FIELD": 15, 157 | "GUIDE_ESSENSE": 16, 158 | "GUIDE_CHANGEJOB": 17, 159 | "GUIDE_APP_GUILD": 18, 160 | "GUIDE_EVENT_MOUSE_MOVE": 19, 161 | "GUIDE_EVENT_KEY_WALK": 20, 162 | "GUIDE_EVENT_MOUSE_CAMERAMOVE": 21, 163 | "GUIDE_EVENT_WORLD_MAP": 22, 164 | "GUIDE_EVENT_WORLD_NAVI": 23, 165 | "GUIDE_EVENT_NORMAL_CHAT": 24, 166 | "GUIDE_EVENT_SHOUT_CHAT": 25, 167 | "GUIDE_EVENT_TROUPE_CHAT": 26, 168 | "GUIDE_EVENT_WHISPER_CHAT": 27, 169 | "GUIDE_EVENT_GUILD_CHAT": 28, 170 | "GUIDE_EVENT_SYSTEM_CHAT": 29, 171 | "GUIDE_EVENT_FILTER_CHAT": 30, 172 | "GUIDE_EVENT_STATUS_WIN": 31, 173 | "GUIDE_EVENT_CHARACTER_WIN": 32, 174 | "GUIDE_EVENT_INVENTORY_WIN": 33, 175 | "GUIDE_EVENT_QUEST_WIN": 34, 176 | "GUIDE_EVENT_QUICKSLOT": 35, 177 | "GUIDE_EVENT_TROUPE": 36, 178 | "GUIDE_EVENT_TROUPE_WIN": 37, 179 | "GUIDE_EVENT_CHARACTER_MENU": 38, 180 | "GUIDE_EVENT_TRADE": 39, 181 | "GUIDE_EVENT_STORE": 40, 182 | "GUIDE_EVENT_BANK": 41, 183 | "GUIDE_EVENT_MESSENGER": 42, 184 | "GUIDE_EVENT_MOTION": 43, 185 | "GUIDE_EVENT_CHEER": 44, 186 | "MAX_SUMMON": 8, 187 | "UA_ITEM": 0, 188 | "UA_LEVEL": 0, 189 | "UA_QUEST": 0, 190 | "UA_CLASS": 0, 191 | "UA_GENDER": 0, 192 | "UA_TELEPORT": 0, 193 | "UA_QUEST_END": 0, 194 | "UA_PLAYER_ID": 0, 195 | "TOT_RANDOM": 0, 196 | "TOT_NOENDU": 1, 197 | "PK_TIGER": 0, 198 | "PK_LION": 1, 199 | "PK_RABBIT": 2, 200 | "PK_FOX": 3, 201 | "PK_DRAGON": 4, 202 | "PK_GRIFFIN": 5, 203 | "PK_UNICORN": 6, 204 | "PK_MAX": 7, 205 | "TI_GENERIC": 0, 206 | "TI_PET": 1, 207 | "PERIN_VALUE": 100000000, 208 | "POINTTYPE_CAMPUS": 1, 209 | "SEASON_NONE": 0, 210 | "SEASON_SPRING": 1, 211 | "SEASON_SUMMER": 2, 212 | "SEASON_FALL": 3, 213 | "SEASON_WINTER": 4, 214 | "SEASON_MAX": 5 215 | } -------------------------------------------------------------------------------- /resources/defineAttribute.json: -------------------------------------------------------------------------------- 1 | { 2 | "VT_ITEM": 1, 3 | "VT_SKILL": 2, 4 | "CT_START": 1, 5 | "CT_CIRCLE": 2, 6 | "CT_FINISH": 3, 7 | "CT_COUNTER": 4, 8 | "CT_BOOSTER": 5, 9 | "CT_FG": 6, 10 | "CT_STEP": 1, 11 | "CT_GENERAL": 4, 12 | "HD_ONE": 1, 13 | "HD_TWO": 2, 14 | "HD_DUAL": 3, 15 | "AT_SLASH": 1, 16 | "AT_BLOW": 2, 17 | "AT_PIERCE": 3, 18 | "AS_HORIZONTAL": 0, 19 | "AS_VERTICAL": 1, 20 | "AS_DIAGONAL": 2, 21 | "AS_THRUST": 3, 22 | "AS_HEAD": 4, 23 | "AS_CHEST": 5, 24 | "AS_ARM": 6, 25 | "AS_LEG": 7, 26 | "AS_BACK": 8, 27 | "MAX_AS": 9, 28 | "WT_MELEE_SWD": 1, 29 | "WT_MELEE_AXE": 2, 30 | "WT_MELEE_STICK": 3, 31 | "WT_MELEE_KNUCKLE": 4, 32 | "WT_MELEE_STAFF": 5, 33 | "WT_MAGIC_WAND": 6, 34 | "WT_MELEE_YOYO": 20, 35 | "WT_RANGE_BOW": 21, 36 | "WT_MELEE": 7, 37 | "WT_RANGE": 8, 38 | "WT_MAGIC": 9, 39 | "WT_CHEER": 10, 40 | "WT_GUN": 11, 41 | "WT_DOLL": 12, 42 | "WT_EQUIP": 13, 43 | "WT_PROPERTY": 14, 44 | "WT_ACROBAT": 15, 45 | "WT_THROWITEM": 16, 46 | "WT_THROWING": 17, 47 | "WT_SWING": 18, 48 | "WT_KNUCKLE": 19, 49 | "_NONE": 0, 50 | "_FIRE": 1, 51 | "_WATER": 2, 52 | "_ELECTRICITY": 3, 53 | "_WIND": 4, 54 | "_EARTH": 5, 55 | "AR_SHORT": 1, 56 | "AR_LONG": 2, 57 | "AR_FAR": 3, 58 | "AR_RANGE": 4, 59 | "AR_WAND": 5, 60 | "AR_HRANGE": 6, 61 | "AR_HWAND": 7, 62 | "SA_DIRECTDMG": 1, 63 | "SA_OBJCHGPARAMET": 2, 64 | "SA_SELFCHGPARAMET": 3, 65 | "SA_OTHERS": 4, 66 | "ST_MAGIC": 1, 67 | "ST_MIND": 2, 68 | "ST_POISON": 3, 69 | "ST_ELECTRICITY": 4, 70 | "ST_FIRE": 5, 71 | "ST_WIND": 6, 72 | "ST_WATER": 7, 73 | "ST_EARTH": 8, 74 | "ST_DARK": 9, 75 | "ST_LIGHT": 10, 76 | "ST_FIREEARTH": 11, 77 | "ST_ELECWIND": 12, 78 | "ST_EARTHWIND": 13, 79 | "ST_EARTHWATER": 14, 80 | "SR_AFTER": 1, 81 | "SR_BEFORE": 2, 82 | "SRO_DIRECT": 1, 83 | "SRO_REGION": 2, 84 | "SRO_EXTENT": 3, 85 | "SRO_SURROUND": 4, 86 | "SRO_DOUBLE": 5, 87 | "SRO_LINE": 6, 88 | "SRO_AROUND": 7, 89 | "SRO_TROUPE": 8, 90 | "KT_MAGIC": 1, 91 | "KT_SKILL": 2, 92 | "CT_TELEPORT": 1, 93 | "CT_SUMMON": 2, 94 | "RACE_HUMAN": 1, 95 | "RACE_ANIMAL": 2, 96 | "RACE_HUMANOID": 3, 97 | "RACE_MONSTER": 4, 98 | "RACE_UNDEAD": 5, 99 | "RACE_GHOST": 6, 100 | "RACE_INSECT": 7, 101 | "RACE_MECHANIC": 8, 102 | "RACE_ELEMENTAL": 9, 103 | "SIZE_TINY": 1, 104 | "SIZE_SMALL": 2, 105 | "SIZE_MIDDLE": 3, 106 | "SIZE_NORMAL": 4, 107 | "SIZE_TALL": 5, 108 | "SIZE_BIG": 6, 109 | "SIZE_GAINT": 7, 110 | "RANK_LOW": 1, 111 | "RANK_NORMAL": 2, 112 | "RANK_CAPTAIN": 3, 113 | "RANK_BOSS": 4, 114 | "RANK_MIDBOSS": 5, 115 | "RANK_MATERIAL": 6, 116 | "RANK_SUPER": 7, 117 | "RANK_GUARD": 8, 118 | "CHS_NORMAL": 0, 119 | "CHS_GUARDARROW": 0, 120 | "CHS_GUARDBULLET": 0, 121 | "CHS_GROGGY": 0, 122 | "CHS_STUN": 0, 123 | "CHS_ANOMY": 0, 124 | "CHS_STARVE": 0, 125 | "CHS_PLASYARM": 0, 126 | "CHS_MISSING": 0, 127 | "CHS_DARK": 0, 128 | "CHS_LITHOSKIN": 0, 129 | "CHS_INVISIBILITY": 0, 130 | "CHS_POISON": 0, 131 | "CHS_SLOW": 0, 132 | "CHS_DMGREFLECT": 0, 133 | "CHS_DOUBLE": 0, 134 | "CHS_BLEEDING": 0, 135 | "CHS_SILENT": 0, 136 | "CHS_DMG_COUNTERATTACK": 0, 137 | "CHS_ATK_COUNTERATTACK": 0, 138 | "CHS_LOOT": 0, 139 | "CHS_SETSTONE": 0, 140 | "CHS_DEBUFFALL": 0, 141 | "CHS_DARK_POISON": 0, 142 | "CHS_DARK_POISON_STUN": 0, 143 | "CHS_LOOT_SLOW": 0, 144 | "CHS_DARK_POISON_STUN_BLEEDING": 0, 145 | "CHS_DARK_POISON_STUN_BLEEDING_DEBUFFALL": 0, 146 | "BELLI_PEACEFUL": 1, 147 | "BELLI_CAUTIOUSATTACK": 2, 148 | "BELLI_ACTIVEATTACK": 3, 149 | "BELLI_ALLIANCE": 4, 150 | "BELLI_ACTIVEATTACK_MELEE2X": 5, 151 | "BELLI_ACTIVEATTACK_MELEE": 6, 152 | "BELLI_ACTIVEATTACK_RANGE": 7, 153 | "BELLI_CAUTIOUSATTACK_MELEE2X": 8, 154 | "BELLI_CAUTIOUSATTACK_MELEE": 9, 155 | "BELLI_CAUTIOUSATTACK_RANGE": 10, 156 | "BELLI_MELEE2X": 11, 157 | "BELLI_MELEE": 12, 158 | "BELLI_RANGE": 13, 159 | "BLOOD_RED": 1, 160 | "BLOOD_GREEN": 2, 161 | "BLOOD_BLUE": 3, 162 | "BLOOD_WHITE": 4, 163 | "BLOOD_BLACK": 5, 164 | "SHELTER_BASICZONE": 1, 165 | "SHELTER_TESTZONE": 2, 166 | "SHELTER_GRASS": 3, 167 | "ELEMENTAL_FIRE": 1, 168 | "ELEMENTAL_WATER": 2, 169 | "ELEMENTAL_WIND": 3, 170 | "ELEMENTAL_EARTH": 4, 171 | "ELEMENTAL_LASER": 5, 172 | "ELEMENTAL_DARK": 6, 173 | "ELEMENTAL_ELEC": 7, 174 | "ELEMENTAL_ANGEL_BLUE": 8, 175 | "ELEMENTAL_ANGEL_RED": 9, 176 | "ELEMENTAL_ANGEL_WHITE": 10, 177 | "ELEMENTAL_ANGEL_GREEN": 11, 178 | "RT_ATTACK": 1, 179 | "RT_TIME": 2, 180 | "RT_HEAL": 3, 181 | "DST_STR": 1, 182 | "DST_DEX": 2, 183 | "DST_INT": 3, 184 | "DST_STA": 4, 185 | "DST_YOY_DMG": 5, 186 | "DST_BOW_DMG": 6, 187 | "DST_CHR_RANGE": 7, 188 | "DST_BLOCK_RANGE": 8, 189 | "DST_CHR_CHANCECRITICAL": 9, 190 | "DST_CHR_BLEEDING": 10, 191 | "DST_SPEED": 11, 192 | "DST_ABILITY_MIN": 12, 193 | "DST_ABILITY_MAX": 13, 194 | "DST_BLOCK_MELEE": 14, 195 | "DST_MASTRY_EARTH": 15, 196 | "DST_STOP_MOVEMENT": 16, 197 | "DST_MASTRY_FIRE": 17, 198 | "DST_MASTRY_WATER": 18, 199 | "DST_MASTRY_ELECTRICITY": 19, 200 | "DST_MASTRY_WIND": 20, 201 | "DST_KNUCKLE_DMG": 21, 202 | "DST_PVP_DMG_RATE": 22, 203 | "DST_ATTACKSPEED": 24, 204 | "DST_SWD_DMG": 25, 205 | "DST_ADJDEF": 26, 206 | "DST_RESIST_MAGIC": 27, 207 | "DST_RESIST_ELECTRICITY": 28, 208 | "DST_REFLECT_DAMAGE": 29, 209 | "DST_RESIST_FIRE": 30, 210 | "DST_RESIST_WIND": 31, 211 | "DST_RESIST_WATER": 32, 212 | "DST_RESIST_EARTH": 33, 213 | "DST_AXE_DMG": 34, 214 | "DST_HP_MAX": 35, 215 | "DST_MP_MAX": 36, 216 | "DST_FP_MAX": 37, 217 | "DST_HP": 38, 218 | "DST_MP": 39, 219 | "DST_FP": 40, 220 | "DST_HP_RECOVERY": 41, 221 | "DST_MP_RECOVERY": 42, 222 | "DST_FP_RECOVERY": 43, 223 | "DST_KILL_HP": 44, 224 | "DST_KILL_MP": 45, 225 | "DST_KILL_FP": 46, 226 | "DST_ADJ_HITRATE": 47, 227 | "DST_CLEARBUFF": 49, 228 | "DST_CHR_STEALHP_IMM": 50, 229 | "DST_ATTACKSPEED_RATE": 51, 230 | "DST_HP_MAX_RATE": 52, 231 | "DST_MP_MAX_RATE": 53, 232 | "DST_FP_MAX_RATE": 54, 233 | "DST_CHR_WEAEATKCHANGE": 55, 234 | "DST_CHR_STEALHP": 56, 235 | "DST_CHR_CHANCESTUN": 57, 236 | "DST_AUTOHP": 58, 237 | "DST_CHR_CHANCEDARK": 59, 238 | "DST_CHR_CHANCEPOISON": 60, 239 | "DST_IMMUNITY": 61, 240 | "DST_ADDMAGIC": 62, 241 | "DST_CHR_DMG": 63, 242 | "DST_CHRSTATE": 64, 243 | "DST_PARRY": 65, 244 | "DST_ATKPOWER_RATE": 66, 245 | "DST_EXPERIENCE": 67, 246 | "DST_JUMPING": 68, 247 | "DST_CHR_CHANCESTEALHP": 69, 248 | "DST_CHR_CHANCEBLEEDING": 70, 249 | "DST_RECOVERY_EXP": 71, 250 | "DST_ADJDEF_RATE": 72, 251 | "DST_MP_DEC_RATE": 73, 252 | "DST_FP_DEC_RATE": 74, 253 | "DST_SPELL_RATE": 75, 254 | "DST_CAST_CRITICAL_RATE": 76, 255 | "DST_CRITICAL_BONUS": 77, 256 | "DST_SKILL_LEVEL": 78, 257 | "DST_MONSTER_DMG": 79, 258 | "DST_PVP_DMG": 80, 259 | "DST_MELEE_STEALHP": 81, 260 | "DST_HEAL": 82, 261 | "DST_ATKPOWER": 83, 262 | "DST_ONEHANDMASTER_DMG": 85, 263 | "DST_TWOHANDMASTER_DMG": 86, 264 | "DST_YOYOMASTER_DMG": 87, 265 | "DST_BOWMASTER_DMG": 88, 266 | "DST_KNUCKLEMASTER_DMG": 89, 267 | "DST_HAWKEYE_RATE": 90, 268 | "DST_RESIST_MAGIC_RATE": 91, 269 | "DST_GIFTBOX": 92, 270 | "DST_RESTPOINT_RATE": 93, 271 | "MAX_ADJPARAMARY": 94, 272 | "DST_GOLD": 10000, 273 | "DST_PXP": 10001, 274 | "DST_RESIST_ALL": 10002, 275 | "DST_STAT_ALLUP": 10003, 276 | "DST_HPDMG_UP": 10004, 277 | "DST_DEFHITRATE_DOWN": 10005, 278 | "DST_CURECHR": 10006, 279 | "DST_HP_RECOVERY_RATE": 10007, 280 | "DST_MP_RECOVERY_RATE": 10008, 281 | "DST_FP_RECOVERY_RATE": 10009, 282 | "DST_LOCOMOTION": 10010, 283 | "DST_MASTRY_ALL": 10011, 284 | "DST_ALL_RECOVERY": 10012, 285 | "DST_ALL_RECOVERY_RATE": 10013, 286 | "DST_KILL_ALL": 10014, 287 | "DST_KILL_HP_RATE": 10015, 288 | "DST_KILL_MP_RATE": 10016, 289 | "DST_KILL_FP_RATE": 10017, 290 | "DST_KILL_ALL_RATE": 10018, 291 | "DST_ALL_DEC_RATE": 10019 292 | } -------------------------------------------------------------------------------- /resources/defineEvent.json: -------------------------------------------------------------------------------- 1 | { 2 | "EVENT_SECRETFORTRESS": 0, 3 | "EVENT_PYRAMID": 1, 4 | "EVENT_OX": 3, 5 | "EVENT_OXNEWBIE": 4, 6 | "EVENT_ITEM": 5, 7 | "EVENT_ITEM_UNIQUE": 6, 8 | "EVENT_WARTEST": 9, 9 | "EVENT_FILLTALEEVENT": 10, 10 | "EVENT_MARATHON": 19 11 | } -------------------------------------------------------------------------------- /resources/defineHonor.json: -------------------------------------------------------------------------------- 1 | { 2 | "HI_ELASPED_TIME": 1, 3 | "HI_COUNT_CHECK": 2, 4 | "HI_EARN_TITLE": 3, 5 | "HI_HUNT_MONSTER": 4, 6 | "HI_USE_ITEM": 5, 7 | "HS_COLLECT": 51, 8 | "HS_TRADE": 52, 9 | "HS_HATCHING_EGG": 61, 10 | "HS_COUPLE_COUNT": 62, 11 | "HS_COUPLE_LV": 63, 12 | "HS_PK_COUNT": 64, 13 | "HS_STR": 65, 14 | "HS_STA": 66, 15 | "HS_DEX": 67, 16 | "HS_INT": 68, 17 | "HS_PVP_POINT01": 70, 18 | "HS_PVP_POINT02": 71, 19 | "HS_PVP_POINT03": 72, 20 | "HS_PVP_POINT04": 73, 21 | "HS_PVP_POINT05": 74, 22 | "HS_PVP_POINT06": 75, 23 | "HS_PVP_POINT07": 76, 24 | "HS_PVP_POINT08": 77, 25 | "HS_PVP_POINT09": 78, 26 | "HS_PVP_POINT10": 79, 27 | "HS_JUMP": 80, 28 | "HS_LORD": 92, 29 | "HS_KILL_COUNT": 1, 30 | "HS_USE_COUNT": 1, 31 | "MONSTER_TITLE_MAX": 70, 32 | "ITEM_TITLE_MAX": 70, 33 | "HM_NONE": 0 34 | } -------------------------------------------------------------------------------- /resources/defineItemkind.json: -------------------------------------------------------------------------------- 1 | { 2 | "IK1_GOLD": 0, 3 | "IK1_WEAPON": 1, 4 | "IK1_ARMOR": 2, 5 | "IK1_GENERAL": 3, 6 | "IK1_RIDE": 4, 7 | "IK1_SYSTEM": 5, 8 | "IK1_CHARGED": 6, 9 | "IK1_HOUSING": 7, 10 | "IK2_GOLD": 0, 11 | "IK2_WEAPON_HAND": 1, 12 | "IK2_WEAPON_DIRECT": 2, 13 | "IK2_WEAPON_MAGIC": 3, 14 | "IK2_ARMOR": 7, 15 | "IK2_ARMORETC": 8, 16 | "IK2_CLOTH": 9, 17 | "IK2_CLOTHETC": 10, 18 | "IK2_REFRESHER": 11, 19 | "IK2_POTION": 12, 20 | "IK2_JEWELRY": 13, 21 | "IK2_FOOD": 14, 22 | "IK2_MAGIC": 15, 23 | "IK2_GEM": 16, 24 | "IK2_MATERIAL": 17, 25 | "IK2_TOOLS": 18, 26 | "IK2_SYSTEM": 19, 27 | "IK2_RIDING": 20, 28 | "IK2_MOB": 21, 29 | "IK2_BLINKWING": 22, 30 | "IK2_AIRFUEL": 23, 31 | "IK2_CHARM": 24, 32 | "IK2_BULLET": 25, 33 | "IK2_TEXT": 26, 34 | "IK2_GMTEXT": 27, 35 | "IK2_GENERAL": 28, 36 | "IK2_BUFF": 29, 37 | "IK2_WARP": 30, 38 | "IK2_SKILL": 31, 39 | "IK2_CLOTHWIG": 32, 40 | "IK2_BUFF2": 33, 41 | "IK2_FURNITURE": 34, 42 | "IK2_PAPERING": 35, 43 | "IK2_TOCASH": 36, 44 | "IK2_BUFF_TOGIFT": 37, 45 | "IK2_GUILDHOUSE_FURNITURE": 38, 46 | "IK2_GUILDHOUSE_NPC": 39, 47 | "IK2_GUILDHOUSE_PAPERING": 40, 48 | "IK2_GUILDHOUES_COMEBACK": 41, 49 | "IK3_GOLD": 0, 50 | "IK3_HAND": 1, 51 | "IK3_SWD": 2, 52 | "IK3_AXE": 3, 53 | "IK3_CHEERSTICK": 4, 54 | "IK3_KNUCKLEHAMMER": 5, 55 | "IK3_WAND": 6, 56 | "IK3_STAFF": 7, 57 | "IK3_THSWD": 8, 58 | "IK3_THAXE": 9, 59 | "IK3_VIRTUAL": 10, 60 | "IK3_YOYO": 11, 61 | "IK3_BOW": 12, 62 | "IK3_YOBO": 13, 63 | "IK3_SHIELD": 16, 64 | "IK3_HELMET": 17, 65 | "IK3_SUIT": 18, 66 | "IK3_GAUNTLET": 19, 67 | "IK3_BOOTS": 20, 68 | "IK3_HAT": 21, 69 | "IK3_MASK": 22, 70 | "IK3_SHOES": 23, 71 | "IK3_CLOAK": 24, 72 | "IK3_CLOTH": 57, 73 | "IK3_GLOVE": 58, 74 | "IK3_REFRESHER": 25, 75 | "IK3_POTION": 26, 76 | "IK3_EARRING": 27, 77 | "IK3_NECKLACE": 28, 78 | "IK3_RING": 29, 79 | "IK3_INSTANT": 30, 80 | "IK3_COOKING": 31, 81 | "IK3_ICECEARM": 32, 82 | "IK3_PILL": 59, 83 | "IK3_MAGICTRICK": 33, 84 | "IK3_GEM": 34, 85 | "IK3_DRINK": 35, 86 | "IK3_COLLECTER": 36, 87 | "IK3_ELECARD": 37, 88 | "IK3_DICE": 38, 89 | "IK3_SUPSTONE": 39, 90 | "IK3_BOARD": 40, 91 | "IK3_STICK": 41, 92 | "IK3_EVENTMAIN": 42, 93 | "IK3_QUEST": 43, 94 | "IK3_MAP": 44, 95 | "IK3_BLINKWING": 45, 96 | "IK3_EVENTSUB": 46, 97 | "IK3_TOWNBLINKWING": 47, 98 | "IK3_ACCEL": 48, 99 | "IK3_DELETE": 49, 100 | "IK3_SCROLL": 50, 101 | "IK3_ENCHANTWEAPON": 51, 102 | "IK3_CFLIGHT": 52, 103 | "IK3_BFLIGHT": 53, 104 | "IK3_MAGICBOTH": 54, 105 | "IK3_BCHARM": 55, 106 | "IK3_RCHARM": 56, 107 | "IK3_ARROW": 60, 108 | "IK3_PIERDICE": 61, 109 | "IK3_SOCKETCARD": 62, 110 | "IK3_SOCKETCARD2": 63, 111 | "IK3_TEXT_BOOK": 70, 112 | "IK3_TEXT_SCROLL": 71, 113 | "IK3_TEXT_LETTER": 72, 114 | "IK3_TEXT_UNDYING": 80, 115 | "IK3_TEXT_DISGUISE": 81, 116 | "IK3_TEXT_INVISIBLE": 82, 117 | "IK3_TEXT_GM": 83, 118 | "IK3_BINDS": 84, 119 | "IK3_CREATE_MONSTER": 85, 120 | "IK3_POTION_BUFF_STR": 90, 121 | "IK3_POTION_BUFF_DEX": 91, 122 | "IK3_POTION_BUFF_INT": 92, 123 | "IK3_POTION_BUFF_STA": 93, 124 | "IK3_POTION_BUFF_DEFENSE": 94, 125 | "IK3_ANGEL_BUFF": 95, 126 | "IK3_PET": 100, 127 | "IK3_RANDOM_SCROLL": 101, 128 | "IK3_ULTIMATE": 102, 129 | "IK3_LINK": 104, 130 | "IK3_GENERAL": 118, 131 | "IK3_ENCHANT": 119, 132 | "IK3_EGG": 120, 133 | "IK3_FEED": 121, 134 | "IK3_TICKET": 122, 135 | "IK3_POCKET": 123, 136 | "IK3_BED": 124, 137 | "IK3_SOFA": 125, 138 | "IK3_WARDROBE": 126, 139 | "IK3_CLOSET": 127, 140 | "IK3_TABLE": 128, 141 | "IK3_CABINET": 129, 142 | "IK3_PROPS": 130, 143 | "IK3_WALLPAPER": 131, 144 | "IK3_CARPET": 132, 145 | "IK3_COUPLE_BUFF": 133, 146 | "IK3_FUNNYCOIN": 134, 147 | "IK3_FLOWER": 135, 148 | "IK3_BALLOON": 136, 149 | "IK3_WING": 137, 150 | "IK3_VIS": 138, 151 | "IK3_TS_BUFF": 139, 152 | "IK3_TELEPORTER": 140, 153 | "IK3_REST": 141, 154 | "IK3_DESK": 142, 155 | "IK3_CHAIR": 143, 156 | "IK3_CASE": 144, 157 | "IK3_BATH": 145, 158 | "IK3_DRAWER": 146, 159 | "IK3_CRYSTAL": 147, 160 | "IK3_KEY": 148, 161 | "MAX_ITEM_KIND3": 149, 162 | "MAX_UNIQUE_SIZE": 400, 163 | "CK1_CHEST": 0, 164 | "CK1_DOOR": 1, 165 | "CK1_TRIGGER": 2, 166 | "CK1_HOUSING": 3, 167 | "CK1_GUILD_HOUSE": 4, 168 | "CK2_FADE": 1, 169 | "CK2_KEEP": 2, 170 | "CK3_FULL": 1, 171 | "CK3_HALF": 2, 172 | "WEAPON_GENERAL": 0, 173 | "WEAPON_UNIQUE": 1, 174 | "WEAPON_ULTIMATE": 2, 175 | "ARMOR_SET": 3, 176 | "PET_VIS": 1 177 | } -------------------------------------------------------------------------------- /resources/defineJob.json: -------------------------------------------------------------------------------- 1 | { 2 | "JTYPE_BASE": 0, 3 | "JTYPE_EXPERT": 1, 4 | "JTYPE_PRO": 2, 5 | "JTYPE_TROUPE": 3, 6 | "JTYPE_COMMON": 4, 7 | "JTYPE_MASTER": 5, 8 | "JTYPE_HERO": 6, 9 | "MAX_JOB_SKILL": 3, 10 | "MAX_EXPERT_SKILL": 20, 11 | "MAX_PRO_SKILL": 20, 12 | "MAX_TROUPE_SKILL": 9, 13 | "MAX_MASTER_SKILL": 1, 14 | "MAX_HERO_SKILL": 1, 15 | "MAX_JOB_LEVEL": 15, 16 | "MAX_EXP_LEVEL": 45, 17 | "MAX_PRO_LEVEL": 30, 18 | "MAX_TROUPE_LEVEL": 1, 19 | "MAX_LEGEND_LEVEL": 121, 20 | "MAX_MONSTER_LEVEL": 160, 21 | "MAX_LEVEL": 150, 22 | "JOB_VAGRANT": 0, 23 | "MAX_JOBBASE": 1, 24 | "JOB_MERCENARY": 1, 25 | "JOB_ACROBAT": 2, 26 | "JOB_ASSIST": 3, 27 | "JOB_MAGICIAN": 4, 28 | "JOB_PUPPETEER": 5, 29 | "MAX_EXPERT": 6, 30 | "JOB_KNIGHT": 6, 31 | "JOB_BLADE": 7, 32 | "JOB_JESTER": 8, 33 | "JOB_RANGER": 9, 34 | "JOB_RINGMASTER": 10, 35 | "JOB_BILLPOSTER": 11, 36 | "JOB_PSYCHIKEEPER": 12, 37 | "JOB_ELEMENTOR": 13, 38 | "JOB_GATEKEEPER": 14, 39 | "JOB_DOPPLER": 15, 40 | "MAX_PROFESSIONAL": 16, 41 | "JOB_KNIGHT_MASTER": 16, 42 | "JOB_BLADE_MASTER": 17, 43 | "JOB_JESTER_MASTER": 18, 44 | "JOB_RANGER_MASTER": 19, 45 | "JOB_RINGMASTER_MASTER": 20, 46 | "JOB_BILLPOSTER_MASTER": 21, 47 | "JOB_PSYCHIKEEPER_MASTER": 22, 48 | "JOB_ELEMENTOR_MASTER": 23, 49 | "MAX_MASTER": 24, 50 | "JOB_KNIGHT_HERO": 24, 51 | "JOB_BLADE_HERO": 25, 52 | "JOB_JESTER_HERO": 26, 53 | "JOB_RANGER_HERO": 27, 54 | "JOB_RINGMASTER_HERO": 28, 55 | "JOB_BILLPOSTER_HERO": 29, 56 | "JOB_PSYCHIKEEPER_HERO": 30, 57 | "JOB_ELEMENTOR_HERO": 31, 58 | "MAX_HERO": 32, 59 | "MAX_JOB": 16, 60 | "DIS_VAGRANT": 0, 61 | "DIS_SWORD": 1, 62 | "DIS_DOUBLE": 2, 63 | "DIS_CASE": 3, 64 | "DIS_JUGGLING": 4, 65 | "DIS_YOYO": 5, 66 | "DIS_RIFLE": 6, 67 | "DIS_MARIONETTE": 7, 68 | "DIS_BOW": 32, 69 | "DIS_SHIELD": 8, 70 | "DIS_DANCE": 9, 71 | "DIS_ACROBATIC": 10, 72 | "DIS_SUPPORT": 23, 73 | "DIS_HEAL": 11, 74 | "DIS_CHEER": 12, 75 | "DIS_ACTING": 13, 76 | "DIS_POSTER": 14, 77 | "DIS_FIRE": 15, 78 | "DIS_WIND": 16, 79 | "DIS_WATER": 17, 80 | "DIS_EARTH": 18, 81 | "DIS_ELECTRICITY": 24, 82 | "DIS_STRINGDANCE": 19, 83 | "DIS_GIGAPUPPET": 20, 84 | "DIS_KNUCKLE": 21, 85 | "DIS_MAGIC": 22, 86 | "DIS_MULTY": 23, 87 | "DIS_PSYCHIC": 24, 88 | "DIS_CURSE": 25, 89 | "DIS_HOLY": 26, 90 | "DIS_TWOHANDWEAPON": 27, 91 | "DIS_TWOHANDSWORD": 28, 92 | "DIS_TWOHANDAXE": 29, 93 | "DIS_DOUBLESWORD": 30, 94 | "DIS_DOUBLEAXE": 31, 95 | "TRO_MASTER": 0, 96 | "TRO_MEMBERE": 1, 97 | "GUD_MASTER": 0, 98 | "GUD_KINGPIN": 1, 99 | "GUD_CAPTAIN": 2, 100 | "GUD_SUPPORTER": 3, 101 | "GUD_ROOKIE": 4 102 | } -------------------------------------------------------------------------------- /resources/defineNeuz.json: -------------------------------------------------------------------------------- 1 | { 2 | "AII_NONE": 0, 3 | "AII_MOVER": 1, 4 | "AII_MONSTER": 2, 5 | "AII_HUMAN": 3, 6 | "AII_CLOCKWORKS": 4, 7 | "AII_PET": 5, 8 | "AII_BIGMUSCLE": 6, 9 | "AII_KRRR": 7, 10 | "AII_BEAR": 8, 11 | "AII_EGG": 9, 12 | "AII_METEONYKER": 10, 13 | "AII_VER2_TYPE0": 100, 14 | "GRP_ALL": 0, 15 | "GRP_SOLO": 1, 16 | "GRP_PARTY": 2, 17 | "GRP_GUILD": 3, 18 | "PARTS_HEAD": 0, 19 | "PARTS_HAIR": 1, 20 | "PARTS_UPPER_BODY": 2, 21 | "PARTS_LOWER_BODY": 3, 22 | "PARTS_HAND": 4, 23 | "PARTS_FOOT": 5, 24 | "PARTS_CAP": 6, 25 | "PARTS_ROBE": 7, 26 | "PARTS_CLOAK": 8, 27 | "PARTS_LWEAPON": 9, 28 | "PARTS_RWEAPON": 10, 29 | "PARTS_SHIELD": 11, 30 | "PARTS_MASK": 12, 31 | "PARTS_RIDE": 13, 32 | "PARTS_CAP2": 14, 33 | "PARTS_UPPER2": 15, 34 | "PARTS_LOWER2": 16, 35 | "PARTS_HAND2": 17, 36 | "PARTS_FOOT2": 18, 37 | "PARTS_NECKLACE1": 19, 38 | "PARTS_RING1": 20, 39 | "PARTS_RING2": 21, 40 | "PARTS_EARRING1": 22, 41 | "PARTS_EARRING2": 23, 42 | "PARTS_PROPERTY": 24, 43 | "PARTS_BULLET": 25, 44 | "PARTS_HAT": 26, 45 | "PARTS_CLOTH": 27, 46 | "PARTS_GLOVE": 28, 47 | "PARTS_BOOTS": 29, 48 | "PARTS_CLOAK2": 30, 49 | "MAX_HUMAN_PARTS": 31, 50 | "SRT_NONE": 0, 51 | "SRT_LODESTAR": 1, 52 | "SRT_LODELOGHT": 2, 53 | "SRT_STATION": 3, 54 | "SRT_WEAPON": 4, 55 | "SRT_SHIELD": 5, 56 | "SRT_FOOD": 6, 57 | "SRT_MAGIC": 7, 58 | "SRT_GENERAL": 8, 59 | "SRT_PUBLICOFFICE": 9, 60 | "SRT_QUESTOFFICE": 10, 61 | "SRT_DUNGEON": 11, 62 | "SRT_BUCKLER": 12, 63 | "SRT_WARPZONE": 13, 64 | "MAX_STRUCTURE": 20, 65 | "QS_BEGIN": 0, 66 | "MMI_DIALOG": 0, 67 | "MMI_QUEST": 1, 68 | "MMI_TRADE": 2, 69 | "MMI_FIGHT": 3, 70 | "MMI_MESSAGE": 4, 71 | "MMI_ADD_MESSENGER": 5, 72 | "MMI_INVITE_PARTY": 6, 73 | "MMI_INVITE_COMPANY": 7, 74 | "MMI_MARKING": 8, 75 | "MMI_BANKING": 9, 76 | "MMI_DUEL": 10, 77 | "MMI_DUEL_PARTY": 11, 78 | "MMI_TRACE": 12, 79 | "MMI_BEAUTYSHOP": 13, 80 | "MMI_REPAIR": 14, 81 | "MMI_GUILDBANKING": 15, 82 | "MMI_RANK_GUILD": 16, 83 | "MMI_RANK_WAR": 17, 84 | "MMI_RANK_INFO": 18, 85 | "MMI_UPGRADE": 19, 86 | "MMI_CHANGEELEM": 20, 87 | "MMI_INPUT_REWARD": 21, 88 | "MMI_SHOW_REWARD": 22, 89 | "MMI_PIERCING": 23, 90 | "MMI_QUERYEQUIP": 24, 91 | "MMI_POST": 25, 92 | "MMI_GUILDWAR_APP": 26, 93 | "MMI_GUILDWAR_STATE": 27, 94 | "MMI_GUILDWAR_CANCEL": 28, 95 | "MMI_GUILDWAR_JOIN": 29, 96 | "MMI_GUILDCOMBAT_RANKING": 30, 97 | "MMI_CHEER": 31, 98 | "MMI_PIERCING_REMOVE": 32, 99 | "MMI_GUILDCOMBAT_SELECTION": 33, 100 | "MMI_GUILDCOMBAT_JACKPOT": 34, 101 | "MMI_GUILDCOMBAT_JACKPOT2": 35, 102 | "MMI_GUILDCOMBAT_BESTPLAYER": 36, 103 | "MMI_GUILDCOMBAT_INFO_BOARD1": 37, 104 | "MMI_GUILDCOMBAT_INFO_BOARD2": 38, 105 | "MMI_GUILDCOMBAT_INFO_BOARD3": 39, 106 | "MMI_GUILDCOMBAT_INFO_TEX": 40, 107 | "MMI_GUILDCOMBAT_PENYA_RETURN": 41, 108 | "MMI_BEAUTYSHOP_SKIN": 42, 109 | "MMI_SUMMON_ANGEL": 43, 110 | "MMI_KAWIBAWIBO": 44, 111 | "MMI_FINDWORD": 45, 112 | "MMI_FIVESYSTEM": 46, 113 | "MMI_REASSEMBLE": 47, 114 | "MMI_PET_FEED": 48, 115 | "MMI_PET_STATUS": 49, 116 | "MMI_PET_RELEASE": 50, 117 | "MMI_SMELT_MIXJEWEL": 51, 118 | "MMI_SMELT_JEWEL": 52, 119 | "MMI_SMELT_EXTRACTION": 53, 120 | "MMI_SMELT_CHANGEGWEAPON": 54, 121 | "MMI_SMELT_CHANGEUWEAPON": 55, 122 | "MMI_LEGEND_SKILLUP": 56, 123 | "MMI_EVENT_MAY": 57, 124 | "MMI_PET_FOODMILL": 58, 125 | "MMI_ATTRIBUTE": 59, 126 | "MMI_GUILDCOMBAT_1TO1_OFFER": 60, 127 | "MMI_GUILDCOMBAT_1TO1_CANCEL": 61, 128 | "MMI_GUILDCOMBAT_1TO1_OFFERSTATE": 62, 129 | "MMI_GUILDCOMBAT_1TO1_SELECTION": 63, 130 | "MMI_GUILDCOMBAT_1TO1_ENTRANCE": 64, 131 | "MMI_GUILDCOMBAT_1TO1_GUIDE_TEX": 65, 132 | "MMI_GUILDCOMBAT_1TO1_GUIDE_PRIZE": 66, 133 | "MMI_GUILDCOMBAT_1TO1_GUIDE_ENTRY": 67, 134 | "MMI_GUILDCOMBAT_1TO1_GUIDE_RULE": 68, 135 | "MMI_GUILDCOMBAT_1TO1_GUIDE_WIN": 69, 136 | "MMI_GUILDCOMBAT_1TO1_REPAYMENT": 70, 137 | "MMI_LVREQDOWN_CANCEL": 71, 138 | "MMI_SMELT_REMOVE_PIERCING": 72, 139 | "MMI_ITEM_AWAKENING": 73, 140 | "MMI_NPC_BUFF": 74, 141 | "MMI_COLLECT01": 75, 142 | "MMI_COLLECT02": 76, 143 | "MMI_PET_RES": 77, 144 | "MMI_PET_RES01": 78, 145 | "MMI_BLESSING_CANCEL": 79, 146 | "MMI_ARENA_ENTER": 80, 147 | "MMI_ARENA_EXIT": 81, 148 | "MMI_EVENT_HAPPY_TWN": 82, 149 | "MMI_EVENT_HAPPY_USA": 83, 150 | "MMI_SECRET_OFFER": 84, 151 | "MMI_SECRET_OFFERSTATE": 85, 152 | "MMI_SECRET_SELECTION": 86, 153 | "MMI_SECRET_ENTRANCE": 87, 154 | "MMI_SECRET_TAXRATES_CHANGE": 88, 155 | "MMI_SECRET_TAXRATES_CHECK": 89, 156 | "MMI_SECRET_BOARD": 90, 157 | "MMI_LORD_STATE": 91, 158 | "MMI_LORD_TENDER": 92, 159 | "MMI_LORD_VOTE": 93, 160 | "MMI_LORD_EVENT": 94, 161 | "MMI_SMELT_REMOVE_JEWEL": 95, 162 | "MMI_PET_EGG01": 96, 163 | "MMI_HEAVEN_TOWER": 97, 164 | "MMI_EXCHANGE_WEAPONCARD": 98, 165 | "MMI_EXCHANGE_ARMORCARD": 99, 166 | "MMI_SECRET_ENTRANCE_1": 100, 167 | "MMI_LORD_INFO": 101, 168 | "MMI_LORD_RPINFO": 102, 169 | "MMI_SECRET_CANCEL": 103, 170 | "MMI_PET_AWAK_CANCEL": 104, 171 | "MMI_FASHION_PHP": 105, 172 | "MMI_EVENT_CARD_KOR": 106, 173 | "MMI_LORD_RAINBOWAPPLICATION": 107, 174 | "MMI_LORD_RAINBOWWAIT": 108, 175 | "MMI_LORD_RAINBOWRULE": 109, 176 | "MMI_LORD_RAINBOWTOPTEN": 110, 177 | "MMI_LORD_RAINBOWWIN": 111, 178 | "MMI_LORD_RAINBOWEND": 112, 179 | "MMI_LORD_RAINBOW_KAWIBAWIBO": 113, 180 | "MMI_LORD_RAINBOW_DICE": 114, 181 | "MMI_LORD_RAINBOW_ARITHMETIC": 115, 182 | "MMI_LORD_RAINBOW_STOPWATCH": 116, 183 | "MMI_LORD_RAINBOW_TYPING": 117, 184 | "MMI_LORD_RAINBOW_CARD": 118, 185 | "MMI_LORD_RAINBOW_LADDER": 119, 186 | "MMI_RENAME_CANCEL": 120, 187 | "MMI_EVENT_TRADE_KOR": 121, 188 | "MMI_VISIT_MYROOM": 122, 189 | "MMI_VISIT_FRIEND": 123, 190 | "MMI_RETURNTO_WORLD": 124, 191 | "MMI_EVENT_RESTATE01": 125, 192 | "MMI_ITEM_TRANSY": 126, 193 | "MMI_EVENT_FIRSTTERM01": 127, 194 | "MMI_EVENT_ALICE01": 128, 195 | "MMI_EVENT_OASIS01": 129, 196 | "MMI_EVENT_FLOWER01": 130, 197 | "MMI_EVENT_ALICE02": 131, 198 | "MMI_EVENT_TRADE_USA": 132, 199 | "MMI_EVENT_TRADE_ASIA": 133, 200 | "MMI_SMELT_SAFETY_GENERAL": 134, 201 | "MMI_SMELT_SAFETY_ACCESSORY": 135, 202 | "MMI_SMELT_SAFETY_PIERCING": 136, 203 | "MMI_QUIZ_ENTRANCE": 137, 204 | "MMI_QUIZ_TELE_QUIZZONE": 138, 205 | "MMI_QUIZ_EXIT": 139, 206 | "MMI_CHANGTICKET01": 140, 207 | "MMI_BUFFPET_STATUS": 141, 208 | "MMI_BUFFPET_RELEASE": 142, 209 | "MMI_GHOUSE_INFO": 143, 210 | "MMI_GHOUSE_REINSTALL": 144, 211 | "MMI_GHOUSE_RECALL": 145, 212 | "MMI_TELEPORTER": 146, 213 | "MMI_GUILDHOUSE_SALE": 147, 214 | "MMI_GUILDHOUSE_CHARGE": 148, 215 | "MMI_GUILDHOUSE_ENTER": 149, 216 | "MMI_GUILDHOUSE_OUT": 150, 217 | "MMI_INVITE_CAMPUS": 151, 218 | "MMI_REMOVE_CAMPUS": 152, 219 | "MMI_CHANGE_CHRISTMAS01": 153, 220 | "MMI_GHOUSE_BED": 154, 221 | "MMI_GHOUSE_TABLE": 155, 222 | "MMI_GHOUSE_SOFA": 156, 223 | "MMI_GHOUSE_DESK": 157, 224 | "MMI_GHOUSE_CHAIR": 158, 225 | "MMI_GHOUSE_CASE": 159, 226 | "MMI_GHOUSE_CLOSET": 160, 227 | "MMI_GHOUSE_WARDROBE": 161, 228 | "MMI_GHOUSE_BATH": 162, 229 | "MMI_GHOUSE_DRAWER": 163, 230 | "MMI_GHOUSE_CARPET": 164, 231 | "MMI_GHOUSE_WALLPAPER": 165, 232 | "MMI_EVENT_FITA": 166, 233 | "MMI_SMELT_SAFETY_ELEMENT": 167, 234 | "MMI_EVENT_LUCKYBOX": 168, 235 | "MMI_CHANGEGG01": 169, 236 | "MAX_MOVER_MENU": 170, 237 | "MGI_APPELL_UP": 0, 238 | "MGI_APPELL_DOWN": 1, 239 | "MGI_CLASS_UP": 2, 240 | "MGI_CLASS_DOWN": 3, 241 | "MGI_GUILD_LEAVE": 4, 242 | "MGI_NICKNAME": 5, 243 | "MGI_CHG_MASTER": 6, 244 | "MGI_GUILD_MENU": 7, 245 | "MOT_BASE_NONE": 0, 246 | "MOT_BASE_BATTLE": 1, 247 | "MOT_BASE_SIT": 2, 248 | "MOT_BASE_PICKUP": 3, 249 | "MOT_BASE_HANDSHAKE": 4, 250 | "MOT_BASE_AMBIGUOUS": 5, 251 | "MOT_BASE_YES": 6, 252 | "MOT_BASE_NO": 7, 253 | "MOT_BASE_SPRINT": 8, 254 | "MOT_BASE_PARADEREST": 9, 255 | "MOT_BASE_ATTENTION": 10, 256 | "MOT_BASE_SCISSORS": 11, 257 | "MOT_BASE_ROCK": 12, 258 | "MOT_BASE_PAPER": 13, 259 | "MOT_BASE_POINTWAYNORTH": 14, 260 | "MOT_BASE_POINTWAYSOUTH": 15, 261 | "MOT_BASE_POINTWAYEAST": 16, 262 | "MOT_BASE_POINTWAYWEST": 17, 263 | "MOT_BASE_KNEEDOWN": 18, 264 | "MOT_BASE_PUSHUP": 19, 265 | "MOT_BASE_HIPKIDDING": 20, 266 | "MOT_BASE_JAB": 21, 267 | "MOT_BASE_HEAD": 22, 268 | "MOT_BASE_RISESWORD": 23, 269 | "MOT_BASE_COLLECT": 24, 270 | "MOT_BASE_ESCAPE": 25, 271 | "MOT_BASE_CHEER": 26, 272 | "MOT_BASE_AUTOATTACK": 27, 273 | "MOT_BASE_COUPLE": 28, 274 | "MTI_STAND": 0, 275 | "MTI_IDLE1": 1, 276 | "MTI_IDLE2": 2, 277 | "MTI_WALK": 3, 278 | "MTI_BACK": 4, 279 | "MTI_RUN": 5, 280 | "MTI_DMG1": 6, 281 | "MTI_DMG2": 7, 282 | "MTI_DIE1": 8, 283 | "MTI_DMGLIVE": 9, 284 | "MTI_DMGDIE": 10, 285 | "MTI_DMGFLY": 11, 286 | "MTI_GROGGY": 12, 287 | "MTI_JUMP1": 13, 288 | "MTI_JUMP2": 14, 289 | "MTI_JUMP3": 15, 290 | "MTI_JUMP4": 16, 291 | "MTI_IN": 17, 292 | "MTI_OUT": 18, 293 | "MTI_SWIM": 19, 294 | "MTI_SIT": 20, 295 | "MTI_SITSTAND": 21, 296 | "MTI_GETUP": 22, 297 | "MTI_ATK1": 23, 298 | "MTI_ATK2": 24, 299 | "MTI_ATK3": 25, 300 | "MTI_ATK4": 26, 301 | "MTI_ATK5": 27, 302 | "MTI_ATK6": 28, 303 | "MTI_ATK7": 29, 304 | "MTI_ATK8": 30, 305 | "MTI_FSTAND1_A": 31, 306 | "MTI_FIDLE1_A": 32, 307 | "MTI_FIDLE2_A": 33, 308 | "MTI_FLTURN1_A": 34, 309 | "MTI_FLTURN2_A": 35, 310 | "MTI_FRTURN1_A": 36, 311 | "MTI_FRTURN2_A": 37, 312 | "MTI_FRUNNING1_A": 38, 313 | "MTI_FRUNNING2_A": 39, 314 | "MTI_FDMG1_A": 40, 315 | "MTI_FATK1_A": 41, 316 | "MTI_FATK2_A": 42, 317 | "MTI_FATK3_A": 43, 318 | "MTI_FSTAND1_B": 61, 319 | "MTI_FIDLE1_B": 62, 320 | "MTI_FIDLE2_B": 63, 321 | "MTI_FLTURN1_B": 64, 322 | "MTI_FLTURN2_B": 65, 323 | "MTI_FRTURN1_B": 66, 324 | "MTI_FRTURN2_B": 67, 325 | "MTI_FRUNNING1_B": 68, 326 | "MTI_FRUNNING2_B": 69, 327 | "MTI_FDMG1_B": 70, 328 | "MTI_FATK1_B": 71, 329 | "MTI_FATK2_B": 72, 330 | "MTI_FATK3_B": 73, 331 | "MTI_FSTAND1_C": 91, 332 | "MTI_FIDLE1_C": 92, 333 | "MTI_FIDLE2_C": 93, 334 | "MTI_FLTURN1_C": 94, 335 | "MTI_FLTURN2_C": 95, 336 | "MTI_FRTURN1_C": 96, 337 | "MTI_FRTURN2_C": 97, 338 | "MTI_FRUNNING1_C": 98, 339 | "MTI_FRUNNING2_C": 99, 340 | "MTI_FDMG1_C": 100, 341 | "MTI_FATK1_C": 101, 342 | "MTI_FATK2_C": 102, 343 | "MTI_FATK3_C": 103, 344 | "MTI_PICKUP": 121, 345 | "MTI_HANDSHAKE": 122, 346 | "MTI_AMBIGUOUS": 123, 347 | "MTI_YES": 124, 348 | "MTI_NO": 125, 349 | "MTI_SPRINT": 126, 350 | "MTI_PARADEREST": 127, 351 | "MTI_ATTENTION": 128, 352 | "MTI_SCISSORS": 129, 353 | "MTI_ROCK": 130, 354 | "MTI_PAPER": 131, 355 | "MTI_POINTWAYNORTH": 132, 356 | "MTI_POINTWAYSOUTH": 133, 357 | "MTI_POINTWAYEAST": 134, 358 | "MTI_POINTWAYWEST": 135, 359 | "MTI_KNEEDOWN": 136, 360 | "MTI_PUSHUP": 137, 361 | "MTI_HIPKIDDING": 138, 362 | "MTI_JAB": 139, 363 | "MTI_HEAD": 140, 364 | "MTI_RISESWORD": 141, 365 | "MTI_COLLECT": 142, 366 | "MTI_APPEAR": 143, 367 | "MTI_APPEAR2": 144, 368 | "MTI_CHEERSAME": 145, 369 | "MTI_CHEEROTHER": 146, 370 | "MTI_FALL": 147, 371 | "MTI_LOGOUT": 194, 372 | "MTI_ACCLAIM": 195, 373 | "MTI_CLAPDOWN": 196, 374 | "MTI_CLAPUP": 197, 375 | "MTI_LEVELUP": 198, 376 | "MTI_STAND2": 199, 377 | "MTI_FLYWALK": 231, 378 | "MTI_FLYBACK": 232, 379 | "MTI_FLYRUN": 233, 380 | "MTI_STAND_01": 200, 381 | "MTI_IDLE1_01": 201, 382 | "MTI_IDLE2_01": 202, 383 | "MTI_WALK_01": 203, 384 | "MTI_BACK_01": 204, 385 | "MTI_RUN_01": 205, 386 | "MTI_DMG1_01": 206, 387 | "MTI_DMG2_01": 207, 388 | "MTI_DIE1_01": 208, 389 | "MTI_DMGLIVE_01": 209, 390 | "MTI_DMGDIE_01": 210, 391 | "MTI_DMGFLY_01": 211, 392 | "MTI_GROGGY_01": 212, 393 | "MTI_JUMP1_01": 213, 394 | "MTI_JUMP2_01": 214, 395 | "MTI_JUMP3_01": 215, 396 | "MTI_JUMP4_01": 216, 397 | "MTI_IN_01": 217, 398 | "MTI_OUT_01": 218, 399 | "MTI_SWIM_01": 219, 400 | "MTI_SIT_01": 220, 401 | "MTI_SITSTAND_01": 221, 402 | "MTI_GETUP_01": 222, 403 | "MTI_ATK1_01": 223, 404 | "MTI_ATK2_01": 224, 405 | "MTI_ATK3_01": 225, 406 | "MTI_ATK4_01": 226, 407 | "MTI_ATK5_01": 227, 408 | "MTI_ATK6_01": 228, 409 | "MTI_ATK7_01": 229, 410 | "MTI_ATK8_01": 230, 411 | "MTI_STAND_02": 300, 412 | "MTI_IDLE1_02": 301, 413 | "MTI_IDLE2_02": 302, 414 | "MTI_WALK_02": 303, 415 | "MTI_BACK_02": 304, 416 | "MTI_RUN_02": 305, 417 | "MTI_DMG1_02": 306, 418 | "MTI_DMG2_02": 307, 419 | "MTI_DIE1_02": 308, 420 | "MTI_DMGLIVE_02": 309, 421 | "MTI_DMGDIE_02": 310, 422 | "MTI_DMGFLY_02": 311, 423 | "MTI_GROGGY_02": 312, 424 | "MTI_JUMP1_02": 313, 425 | "MTI_JUMP2_02": 314, 426 | "MTI_JUMP3_02": 315, 427 | "MTI_JUMP4_02": 316, 428 | "MTI_IN_02": 317, 429 | "MTI_OUT_02": 318, 430 | "MTI_SWIM_02": 319, 431 | "MTI_SIT_02": 320, 432 | "MTI_SITSTAND_02": 321, 433 | "MTI_GETUP_02": 322, 434 | "MTI_ATK1_02": 323, 435 | "MTI_ATK2_02": 324, 436 | "MTI_ATK3_02": 325, 437 | "MTI_ATK4_02": 326, 438 | "MTI_ATK5_02": 327, 439 | "MTI_ATK6_02": 328, 440 | "MTI_ATK7_02": 329, 441 | "MTI_ATK8_02": 330, 442 | "MTI_STAND_03": 400, 443 | "MTI_IDLE1_03": 401, 444 | "MTI_IDLE2_03": 402, 445 | "MTI_WALK_03": 403, 446 | "MTI_BACK_03": 404, 447 | "MTI_RUN_03": 405, 448 | "MTI_DMG1_03": 406, 449 | "MTI_DMG2_03": 407, 450 | "MTI_DIE1_03": 408, 451 | "MTI_DMGLIVE_03": 409, 452 | "MTI_DMGDIE_03": 410, 453 | "MTI_DMGFLY_03": 411, 454 | "MTI_GROGGY_03": 412, 455 | "MTI_JUMP1_03": 413, 456 | "MTI_JUMP2_03": 414, 457 | "MTI_JUMP3_03": 415, 458 | "MTI_JUMP4_03": 416, 459 | "MTI_IN_03": 417, 460 | "MTI_OUT_03": 418, 461 | "MTI_SWIM_03": 419, 462 | "MTI_SIT_03": 420, 463 | "MTI_SITSTAND_03": 421, 464 | "MTI_GETUP_03": 422, 465 | "MTI_ATK1_03": 423, 466 | "MTI_ATK2_03": 424, 467 | "MTI_ATK3_03": 425, 468 | "MTI_ATK4_03": 426, 469 | "MTI_ATK5_03": 427, 470 | "MTI_ATK6_03": 428, 471 | "MTI_ATK7_03": 429, 472 | "MTI_ATK8_03": 430, 473 | "MTI_STAND_04": 500, 474 | "MTI_IDLE1_04": 501, 475 | "MTI_IDLE2_04": 502, 476 | "MTI_WALK_04": 503, 477 | "MTI_BACK_04": 504, 478 | "MTI_RUN_04": 505, 479 | "MTI_DMG1_04": 506, 480 | "MTI_DMG2_04": 507, 481 | "MTI_DIE1_04": 508, 482 | "MTI_DMGLIVE_04": 509, 483 | "MTI_DMGDIE_04": 510, 484 | "MTI_DMGFLY_04": 511, 485 | "MTI_GROGGY_04": 512, 486 | "MTI_JUMP1_04": 513, 487 | "MTI_JUMP2_04": 514, 488 | "MTI_JUMP3_04": 515, 489 | "MTI_JUMP4_04": 516, 490 | "MTI_IN_04": 517, 491 | "MTI_OUT_04": 518, 492 | "MTI_SWIM_04": 519, 493 | "MTI_SIT_04": 520, 494 | "MTI_SITSTAND_04": 521, 495 | "MTI_GETUP_04": 522, 496 | "MTI_ATK1_04": 523, 497 | "MTI_ATK2_04": 524, 498 | "MTI_ATK3_04": 525, 499 | "MTI_ATK4_04": 526, 500 | "MTI_ATK5_04": 527, 501 | "MTI_ATK6_04": 528, 502 | "MTI_ATK7_04": 529, 503 | "MTI_ATK8_04": 530, 504 | "MTI_STAND_05": 600, 505 | "MTI_IDLE1_05": 601, 506 | "MTI_IDLE2_05": 602, 507 | "MTI_WALK_05": 603, 508 | "MTI_BACK_05": 604, 509 | "MTI_RUN_05": 605, 510 | "MTI_DMG1_05": 606, 511 | "MTI_DMG2_05": 607, 512 | "MTI_DIE1_05": 608, 513 | "MTI_DMGLIVE_05": 609, 514 | "MTI_DMGDIE_05": 610, 515 | "MTI_DMGFLY_05": 611, 516 | "MTI_GROGGY_05": 612, 517 | "MTI_JUMP1_05": 613, 518 | "MTI_JUMP2_05": 614, 519 | "MTI_JUMP3_05": 615, 520 | "MTI_JUMP4_05": 616, 521 | "MTI_IN_05": 617, 522 | "MTI_OUT_05": 618, 523 | "MTI_SWIM_05": 619, 524 | "MTI_SIT_05": 620, 525 | "MTI_SITSTAND_05": 621, 526 | "MTI_GETUP_05": 622, 527 | "MTI_ATK1_05": 623, 528 | "MTI_ATK2_05": 624, 529 | "MTI_ATK3_05": 625, 530 | "MTI_ATK4_05": 626, 531 | "MTI_ATK5_05": 627, 532 | "MTI_ATK6_05": 628, 533 | "MTI_ATK7_05": 629, 534 | "MTI_ATK8_05": 630, 535 | "MTI_STAND_06": 700, 536 | "MTI_IDLE1_06": 701, 537 | "MTI_IDLE2_06": 702, 538 | "MTI_WALK_06": 703, 539 | "MTI_BACK_06": 704, 540 | "MTI_RUN_06": 705, 541 | "MTI_DMG1_06": 706, 542 | "MTI_DMG2_06": 707, 543 | "MTI_DIE1_06": 708, 544 | "MTI_DMGLIVE_06": 709, 545 | "MTI_DMGDIE_06": 710, 546 | "MTI_DMGFLY_06": 711, 547 | "MTI_GROGGY_06": 712, 548 | "MTI_JUMP1_06": 713, 549 | "MTI_JUMP2_06": 714, 550 | "MTI_JUMP3_06": 715, 551 | "MTI_JUMP4_06": 716, 552 | "MTI_IN_06": 717, 553 | "MTI_OUT_06": 718, 554 | "MTI_SWIM_06": 719, 555 | "MTI_SIT_06": 720, 556 | "MTI_SITSTAND_06": 721, 557 | "MTI_GETUP_06": 722, 558 | "MTI_ATK1_06": 723, 559 | "MTI_ATK2_06": 724, 560 | "MTI_ATK3_06": 725, 561 | "MTI_ATK4_06": 726, 562 | "MTI_ATK5_06": 727, 563 | "MTI_ATK6_06": 728, 564 | "MTI_ATK7_06": 729, 565 | "MTI_ATK8_06": 730, 566 | "MTI_STAND_07": 800, 567 | "MTI_IDLE1_07": 801, 568 | "MTI_IDLE2_07": 802, 569 | "MTI_WALK_07": 803, 570 | "MTI_BACK_07": 804, 571 | "MTI_RUN_07": 805, 572 | "MTI_DMG1_07": 806, 573 | "MTI_DMG2_07": 807, 574 | "MTI_DIE1_07": 808, 575 | "MTI_DMGLIVE_07": 809, 576 | "MTI_DMGDIE_07": 810, 577 | "MTI_DMGFLY_07": 811, 578 | "MTI_GROGGY_07": 812, 579 | "MTI_JUMP1_07": 813, 580 | "MTI_JUMP2_07": 814, 581 | "MTI_JUMP3_07": 815, 582 | "MTI_JUMP4_07": 816, 583 | "MTI_IN_07": 817, 584 | "MTI_OUT_07": 818, 585 | "MTI_SWIM_07": 819, 586 | "MTI_SIT_07": 820, 587 | "MTI_SITSTAND_07": 821, 588 | "MTI_GETUP_07": 822, 589 | "MTI_ATK1_07": 823, 590 | "MTI_ATK2_07": 824, 591 | "MTI_ATK3_07": 825, 592 | "MTI_ATK4_07": 826, 593 | "MTI_ATK5_07": 827, 594 | "MTI_ATK6_07": 828, 595 | "MTI_ATK7_07": 829, 596 | "MTI_ATK8_07": 830, 597 | "MTI_STAND_08": 900, 598 | "MTI_IDLE1_08": 901, 599 | "MTI_IDLE2_08": 902, 600 | "MTI_WALK_08": 903, 601 | "MTI_BACK_08": 904, 602 | "MTI_RUN_08": 905, 603 | "MTI_DMG1_08": 906, 604 | "MTI_DMG2_08": 907, 605 | "MTI_DIE1_08": 908, 606 | "MTI_DMGLIVE_08": 909, 607 | "MTI_DMGDIE_08": 910, 608 | "MTI_DMGFLY_08": 911, 609 | "MTI_GROGGY_08": 912, 610 | "MTI_JUMP1_08": 913, 611 | "MTI_JUMP2_08": 914, 612 | "MTI_JUMP3_08": 915, 613 | "MTI_JUMP4_08": 916, 614 | "MTI_IN_08": 917, 615 | "MTI_OUT_08": 918, 616 | "MTI_SWIM_08": 919, 617 | "MTI_SIT_08": 920, 618 | "MTI_SITSTAND_08": 921, 619 | "MTI_GETUP_08": 922, 620 | "MTI_ATK1_08": 923, 621 | "MTI_ATK2_08": 924, 622 | "MTI_ATK3_08": 925, 623 | "MTI_ATK4_08": 926, 624 | "MTI_ATK5_08": 927, 625 | "MTI_ATK6_08": 928, 626 | "MTI_ATK7_08": 929, 627 | "MTI_ATK8_08": 930, 628 | "MTI_STAND_09": 2000, 629 | "MTI_IDLE1_09": 2001, 630 | "MTI_IDLE2_09": 2002, 631 | "MTI_WALK_09": 2003, 632 | "MTI_BACK_09": 2004, 633 | "MTI_RUN_09": 2005, 634 | "MTI_DMG1_09": 2006, 635 | "MTI_DMG2_09": 2007, 636 | "MTI_DIE1_09": 2008, 637 | "MTI_DMGLIVE_09": 2009, 638 | "MTI_DMGDIE_09": 2010, 639 | "MTI_DMGFLY_09": 2011, 640 | "MTI_GROGGY_09": 2012, 641 | "MTI_JUMP1_09": 2013, 642 | "MTI_JUMP2_09": 2014, 643 | "MTI_JUMP3_09": 2015, 644 | "MTI_JUMP4_09": 2016, 645 | "MTI_IN_09": 2017, 646 | "MTI_OUT_09": 2018, 647 | "MTI_SWIM_09": 2019, 648 | "MTI_SIT_09": 2020, 649 | "MTI_SITSTAND_09": 2021, 650 | "MTI_GETUP_09": 2022, 651 | "MTI_ATK1_09": 2023, 652 | "MTI_ATK2_09": 2024, 653 | "MTI_ATK3_09": 2025, 654 | "MTI_ATK5_09": 2026, 655 | "MTI_ATK6_09": 2027, 656 | "MTI_ATK7_09": 2028, 657 | "MTI_ATK8_09": 2029, 658 | "MTI_STAND_10": 2100, 659 | "MTI_IDLE1_10": 2101, 660 | "MTI_IDLE2_10": 2102, 661 | "MTI_WALK_10": 2103, 662 | "MTI_BACK_10": 2104, 663 | "MTI_RUN_10": 2105, 664 | "MTI_DMG1_10": 2106, 665 | "MTI_DMG2_10": 2107, 666 | "MTI_DIE1_10": 2108, 667 | "MTI_DMGLIVE_10": 2109, 668 | "MTI_DMGDIE_10": 2110, 669 | "MTI_DMGFLY_10": 2111, 670 | "MTI_GROGGY_10": 2112, 671 | "MTI_JUMP1_10": 2113, 672 | "MTI_JUMP2_10": 2114, 673 | "MTI_JUMP3_10": 2115, 674 | "MTI_JUMP4_10": 2116, 675 | "MTI_IN_10": 2117, 676 | "MTI_OUT_10": 2118, 677 | "MTI_SWIM_10": 2119, 678 | "MTI_SIT_10": 2120, 679 | "MTI_SITSTAND_10": 2121, 680 | "MTI_GETUP_10": 2122, 681 | "MTI_ATK1_10": 2123, 682 | "MTI_ATK2_10": 2124, 683 | "MTI_ATK3_10": 2125, 684 | "MTI_ATK4_10": 2126, 685 | "MTI_ATK5_10": 2127, 686 | "MTI_ATK6_10": 2128, 687 | "MTI_ATK7_10": 2129, 688 | "MTI_ATK8_10": 2130, 689 | "MTI_STAND_11": 2200, 690 | "MTI_IDLE1_11": 2201, 691 | "MTI_IDLE2_11": 2202, 692 | "MTI_WALK_11": 2203, 693 | "MTI_BACK_11": 2204, 694 | "MTI_RUN_11": 2205, 695 | "MTI_DMG1_11": 2206, 696 | "MTI_DMG2_11": 2207, 697 | "MTI_DIE1_11": 2208, 698 | "MTI_DMGLIVE_11": 2209, 699 | "MTI_DMGDIE_11": 2210, 700 | "MTI_DMGFLY_11": 2211, 701 | "MTI_GROGGY_11": 2212, 702 | "MTI_JUMP1_11": 2213, 703 | "MTI_JUMP2_11": 2214, 704 | "MTI_JUMP3_11": 2215, 705 | "MTI_JUMP4_11": 2216, 706 | "MTI_IN_11": 2217, 707 | "MTI_OUT_11": 2218, 708 | "MTI_SWIM_11": 2219, 709 | "MTI_SIT_11": 2220, 710 | "MTI_SITSTAND_11": 2221, 711 | "MTI_GETUP_11": 2222, 712 | "MTI_ATK_11": 2223, 713 | "MTI_ATK5_11": 2224, 714 | "MTI_ATK6_11": 2225, 715 | "MTI_ATK7_11": 2226, 716 | "MTI_ATK8_11": 2227, 717 | "MTI_STAND_12": 2300, 718 | "MTI_IDLE1_12": 2301, 719 | "MTI_IDLE2_12": 2302, 720 | "MTI_WALK_12": 2303, 721 | "MTI_BACK_12": 2304, 722 | "MTI_RUN_12": 2305, 723 | "MTI_DMG1_12": 2306, 724 | "MTI_DMG2_12": 2307, 725 | "MTI_DIE1_12": 2308, 726 | "MTI_DMGLIVE_12": 2309, 727 | "MTI_DMGDIE_12": 2310, 728 | "MTI_DMGFLY_12": 2311, 729 | "MTI_GROGGY_12": 2312, 730 | "MTI_JUMP1_12": 2313, 731 | "MTI_JUMP2_12": 2314, 732 | "MTI_JUMP3_12": 2315, 733 | "MTI_JUMP4_12": 2316, 734 | "MTI_IN_12": 2317, 735 | "MTI_OUT_12": 2318, 736 | "MTI_SWIM_12": 2319, 737 | "MTI_SIT_12": 2320, 738 | "MTI_SITSTAND_12": 2321, 739 | "MTI_GETUP_12": 2322, 740 | "MTI_ATK_12": 2323, 741 | "MTI_ATK5_12": 2324, 742 | "MTI_ATK6_12": 2325, 743 | "MTI_ATK7_12": 2326, 744 | "MTI_ATK8_12": 2327, 745 | "MTI_STAND_13": 2328, 746 | "MTI_IDLE1_13": 2329, 747 | "MTI_IDLE2_13": 2330, 748 | "MTI_WALK_13": 2331, 749 | "MTI_BACK_13": 2332, 750 | "MTI_RUN_13": 2333, 751 | "MTI_DMG1_13": 2334, 752 | "MTI_DMG2_13": 2335, 753 | "MTI_DIE1_13": 2336, 754 | "MTI_DMGLIVE_13": 2337, 755 | "MTI_DMGDIE_13": 2338, 756 | "MTI_DMGFLY_13": 2339, 757 | "MTI_GROGGY_13": 2340, 758 | "MTI_JUMP1_13": 2341, 759 | "MTI_JUMP2_13": 2342, 760 | "MTI_JUMP3_13": 2343, 761 | "MTI_JUMP4_13": 2344, 762 | "MTI_IN_13": 2345, 763 | "MTI_OUT_13": 2346, 764 | "MTI_SWIM_13": 2347, 765 | "MTI_SIT_13": 2348, 766 | "MTI_SITSTAND_13": 2349, 767 | "MTI_GETUP_13": 2350, 768 | "MTI_ATK_13": 2351, 769 | "MTI_ATK5_13": 2352, 770 | "MTI_ATK6_13": 2353, 771 | "MTI_ATK7_13": 2354, 772 | "MTI_ATK8_13": 2355, 773 | "MTI_STAND_14": 2356, 774 | "MTI_IDLE1_14": 2357, 775 | "MTI_IDLE2_14": 2358, 776 | "MTI_WALK_14": 2359, 777 | "MTI_BACK_14": 2360, 778 | "MTI_RUN_14": 2361, 779 | "MTI_DMG1_14": 2362, 780 | "MTI_DMG2_14": 2363, 781 | "MTI_DIE1_14": 2364, 782 | "MTI_DMGLIVE_14": 2365, 783 | "MTI_DMGDIE_14": 2366, 784 | "MTI_DMGFLY_14": 2367, 785 | "MTI_GROGGY_14": 2368, 786 | "MTI_JUMP1_14": 2369, 787 | "MTI_JUMP2_14": 2370, 788 | "MTI_JUMP3_14": 2371, 789 | "MTI_JUMP4_14": 2372, 790 | "MTI_IN_14": 2373, 791 | "MTI_OUT_14": 2374, 792 | "MTI_SWIM_14": 2375, 793 | "MTI_SIT_14": 2376, 794 | "MTI_SITSTAND_14": 2377, 795 | "MTI_GETUP_14": 2378, 796 | "MTI_ATK1_14": 2379, 797 | "MTI_ATK2_14": 2380, 798 | "MTI_ATK5_14": 2381, 799 | "MTI_ATK6_14": 2382, 800 | "MTI_ATK7_14": 2383, 801 | "MTI_ATK8_14": 2384, 802 | "MTA_FSTAND1": 0, 803 | "MTA_FIDLE1": 1, 804 | "MTA_FIDLE2": 2, 805 | "MTA_FLTURN1": 3, 806 | "MTA_FLTURN2": 4, 807 | "MTA_FRTURN1": 5, 808 | "MTA_FRTURN2": 6, 809 | "MTA_FRUNNING1": 7, 810 | "MTA_FRUNNING2": 8, 811 | "MTA_FDMG1": 9, 812 | "MTA_FATK1": 10, 813 | "MTA_FATK2": 11, 814 | "MTA_FATK3": 12, 815 | "MTI_VAG_ONE_CLEANHIT": 1000, 816 | "MTI_VAG_ONE_BRANDISH": 1001, 817 | "MTI_VAG_ONE_OVERCUTTER": 1002, 818 | "MTI_MER_ONE_SPLMASH": 1003, 819 | "MTI_MER_ONE_KEENWHEEL": 1004, 820 | "MTI_MER_ONE_BLINDSIDE": 1005, 821 | "MTI_MER_ONE_SPECIAL": 1006, 822 | "MTI_MER_ONE_SNAKE": 1007, 823 | "MTI_MER_SHIELD_PROTECTION": 1008, 824 | "MTI_MER_SHIELD_PANBARRIER": 1009, 825 | "MTI_MER_ONE_BLOODYSTRIKE": 1010, 826 | "MTI_ASS_HEAL_CASTING01": 1043, 827 | "MTI_ASS_HEAL_CASTING02": 1044, 828 | "MTI_ASS_HEAL_CASTING03": 1045, 829 | "MTI_ASS_CHEER_CASTING01": 1046, 830 | "MTI_ASS_CHEER_CASTING02": 1047, 831 | "MTI_ASS_CHEER_CASTING03": 1048, 832 | "MTI_ASS_RESURRECTION": 1049, 833 | "MTI_ASS_RES_CASTING01": 1050, 834 | "MTI_ASS_RES_CASTING02": 1051, 835 | "MTI_ASS_RES_CASTING03": 1052, 836 | "MTI_ASS_KNU_POWERFIST": 1053, 837 | "MTI_MAG_FIRE_CASTING01": 1063, 838 | "MTI_MAG_FIRE_CASTING02": 1064, 839 | "MTI_MAG_FIRE_CASTING03": 1065, 840 | "MTI_MAG_WIND_CASTING01": 1066, 841 | "MTI_MAG_WIND_CASTING02": 1067, 842 | "MTI_MAG_WIND_CASTING03": 1068, 843 | "MTI_ASS_KNU_BURSTCRACK": 1103, 844 | "MTI_ASS_KNU_TAMPINGHOLE": 1104, 845 | "MTI_KNT_SUP_CASTING01": 1105, 846 | "MTI_KNT_TWO_POWERSWING01": 1106, 847 | "MTI_KNT_TWOSW_EARTHDIVIDER01": 1107, 848 | "MTI_KNT_TWOSW_CHARGE01": 1108, 849 | "MTI_KNT_TWOAX_PAINDEALER01": 1109, 850 | "MTI_KNT_TWOAX_POWERSTUMP01": 1110, 851 | "MTI_BLD_DOUBLE_CROSSSTRIKE01": 1111, 852 | "MTI_BLD_DOUBLE_ARMORPENETRATE01": 1112, 853 | "MTI_BLD_DOUBLESW_SILENTSTRIKE01": 1113, 854 | "MTI_BLD_DOUBLESW_BLADEDANCE01": 1114, 855 | "MTI_BLD_DOUBLEAX_SPRINGATTAKE01": 1115, 856 | "MTI_BLD_DOUBLEAX_HAWKATTAKE01": 1116, 857 | "MTI_BLD_DOUBLE_SONICBLADE01": 1117, 858 | "MTI_BLD_SUP_CASTING01": 1118, 859 | "MTI_RIN_HEAL_CASTING01": 1119, 860 | "MTI_RIN_HEAL_CASTING02": 1120, 861 | "MTI_RIN_HEAL_CASTING03": 1121, 862 | "MTI_RIN_SUP_CASTING01": 1122, 863 | "MTI_RIN_SUP_CASTING02": 1123, 864 | "MTI_RIN_SUP_CASTING03": 1124, 865 | "MTI_RIN_HEAL_CURE01": 1125, 866 | "MTI_RIN_HEAL_CURE02": 1126, 867 | "MTI_RIN_HEAL_CURE03": 1127, 868 | "MTI_RIN_SQU_CASTING01": 1128, 869 | "MTI_RIN_SQU_CASTING02": 1129, 870 | "MTI_RIN_SQU_CASTING03": 1130, 871 | "MTI_BIL_KNU_BELIALSMESHING01": 1131, 872 | "MTI_BIL_KNU_PIERCINGSERPENT01": 1132, 873 | "MTI_BIL_KNU_BLOODFIST01": 1133, 874 | "MTI_BIL_KNU_SONICHAND01": 1134, 875 | "MTI_BIL_PST_CASTING01": 1135, 876 | "MTI_BIL_PST_CASTING02": 1136, 877 | "MTI_BIL_PST_CASTING03": 1137, 878 | "MTI_BIL_PST_SQUARE01": 1138, 879 | "MTI_BIL_PST_SQUARE02": 1139, 880 | "MTI_BIL_PST_SQUARE03": 1140, 881 | "MTI_PSY_NLG_CASTING01": 1145, 882 | "MTI_PSY_NLG_CASTING02": 1146, 883 | "MTI_PSY_NLG_CASTING03": 1147, 884 | "MTI_PSY_PSY_PSYCHI01": 1148, 885 | "MTI_PSY_PSY_PSYCHI02": 1149, 886 | "MTI_PSY_PSY_PSYCHI03": 1150, 887 | "MTI_PSY_PSY_SQUARE01": 1151, 888 | "MTI_PSY_PSY_SQUARE02": 1152, 889 | "MTI_PSY_PSY_SQUARE03": 1153, 890 | "MTI_ELE_FIRE_CASTING01": 1160, 891 | "MTI_ELE_FIRE_CASTING02": 1161, 892 | "MTI_ELE_FIRE_CASTING03": 1162, 893 | "MTI_ELE_ELECTRICITY_CASTING01": 1163, 894 | "MTI_ELE_ELECTRICITY_CASTING02": 1164, 895 | "MTI_ELE_ELECTRICITY_CASTING03": 1165, 896 | "MTI_ELE_EARTH_CASTING01": 1166, 897 | "MTI_ELE_EARTH_CASTING02": 1167, 898 | "MTI_ELE_EARTH_CASTING03": 1168, 899 | "MTI_ELE_WATER_CASTING01": 1169, 900 | "MTI_ELE_WATER_CASTING02": 1170, 901 | "MTI_ELE_WATER_CASTING03": 1171, 902 | "MTI_ELE_WIND_CASTING01": 1172, 903 | "MTI_ELE_WIND_CASTING02": 1173, 904 | "MTI_ELE_WIND_CASTING03": 1174, 905 | "MTI_ELE_MULTI_CASTING01": 1175, 906 | "MTI_ELE_MULTI_CASTING02": 1176, 907 | "MTI_ELE_MULTI_CASTING03": 1177, 908 | "MTI_ACR_SUP_CASTING01": 1178, 909 | "MTI_ACR_BOW_JUNKBOW01": 1179, 910 | "MTI_ACR_BOW_AIMEDSHOT01": 1180, 911 | "MTI_ACR_YOYO_SLOWSTEP01": 1181, 912 | "MTI_ACR_BOW_SILENTSHOT01": 1182, 913 | "MTI_ACR_DEF_SUPPORT01": 1183, 914 | "MTI_ACR_BOW_ARROWRAIN01": 1184, 915 | "MTI_ACR_YOYO_CROSSLINE01": 1185, 916 | "MTI_ACR_BOW_AUTOSHOT01": 1186, 917 | "MTI_ACR_YOYO_SNITCH01": 1187, 918 | "MTI_ACR_YOYO_COUNTER01": 1188, 919 | "MTI_ACR_YOYO_DEADLYSWING01": 1189, 920 | "MTI_ACR_YOYO_PULLING01": 1190, 921 | "MTI_ACR_YOYO_COUNTER02": 1205, 922 | "MTI_JST_SUP_CRITICALSWING01": 1191, 923 | "MTI_JST_SUP_ENCHANT": 1192, 924 | "MTI_JST_YOYO_BACKSTAB01": 1193, 925 | "MTI_JST_YOYO_HITOFPENYA01": 1194, 926 | "MTI_JST_YOYO_ESCAPE01": 1195, 927 | "MTI_JST_YOYO_VATALSTAB01": 1196, 928 | "MTI_RAG_SUP_FASTATTACK01": 1197, 929 | "MTI_RAG_BOW_ICEARROW01": 1198, 930 | "MTI_RAG_BOW_FLAMEARROW01": 1199, 931 | "MTI_RAG_BOW_PIRCINGARROW01": 1200, 932 | "MTI_RAG_BOW_POISONARROW01": 1201, 933 | "MTI_RAG_BOW_SILENTARROW01": 1202, 934 | "MTI_RAG_SUP_NATURE01": 1203, 935 | "MTI_RAG_BOW_TRIPLESHOT01": 1204, 936 | "MTI_FSTAND1_D": 1206, 937 | "MTI_FIDLE1_D": 1207, 938 | "MTI_FIDLE2_D": 1208, 939 | "MTI_FLTURN1_D": 1209, 940 | "MTI_FLTURN2_D": 1210, 941 | "MTI_FRTURN1_D": 1211, 942 | "MTI_FRTURN2_D": 1212, 943 | "MTI_FRUNNING1_D": 1213, 944 | "MTI_FRUNNING2_D": 1214, 945 | "MTI_FDMG1_D": 1215, 946 | "MTI_FATK1_D": 1216, 947 | "MTI_FATK2_D": 1217, 948 | "MTI_FATK3_D": 1218, 949 | "HAIRMESH_01": 0, 950 | "HAIRMESH_02": 1, 951 | "HAIRMESH_03": 2, 952 | "HAIRMESH_04": 3, 953 | "HAIRMESH_05": 4, 954 | "HAIRMESH_06": 5, 955 | "HAIRMESH_07": 6, 956 | "HAIRMESH_08": 7, 957 | "HAIRMESH_09": 8, 958 | "HAIRMESH_10": 9, 959 | "SKINSET_01": 0, 960 | "SKINSET_02": 1, 961 | "SKINSET_03": 2, 962 | "SKINSET_04": 3, 963 | "SKINSET_05": 4, 964 | "HEADTEXTURE_NORMAL": 0, 965 | "HEADTEXTURE_SMILE": 1, 966 | "HEADTEXTURE_RAGE": 2, 967 | "MAX_DEFAULT_HEAD": 5, 968 | "MAX_HEAD": 11, 969 | "MAX_HAIR": 10, 970 | "MAX_BASE_HAIR": 5, 971 | "MAX_SKIN": 2, 972 | "HAIR_COST": 2000000, 973 | "HAIRCOLOR_COST": 4000000, 974 | "CHANGE_FACE_COST": 1000000 975 | } -------------------------------------------------------------------------------- /resources/defineWorld.json: -------------------------------------------------------------------------------- 1 | { 2 | "WI_WORLD_NONE": 0, 3 | "WI_WORLD_MADRIGAL": 1, 4 | "WI_WORLD_KEBARAS": 2, 5 | "WI_WORLD_CISLAND": 3, 6 | "WI_WORLD_RARTESIA": 4, 7 | "WI_WORLD_HEAVEN01": 21, 8 | "WI_WORLD_HEAVEN02": 22, 9 | "WI_WORLD_HEAVEN03": 23, 10 | "WI_WORLD_HEAVEN04": 24, 11 | "WI_WORLD_HEAVEN05": 25, 12 | "WI_WORLD_0425": 100, 13 | "WI_WORLD_TEST": 101, 14 | "WI_WORLD_LUX": 102, 15 | "WI_WORLD_LUX2": 103, 16 | "WI_INSTANCE_OMINOUS": 121, 17 | "WI_INSTANCE_OMINOUS_1": 122, 18 | "WI_INSTANCE_DREADFULCAVE": 123, 19 | "WI_INSTANCE_RUSTIA": 124, 20 | "WI_INSTANCE_RUSTIA_1": 125, 21 | "WI_INSTANCE_LAST_ID": 125, 22 | "WI_DUNGEON_SECRET_0": 151, 23 | "WI_DUNGEON_SECRET_1": 152, 24 | "WI_DUNGEON_SECRET_2": 153, 25 | "WI_DUNGEON_SECRET_3": 154, 26 | "WI_DUNGEON_SECRET_4": 155, 27 | "WI_DUNGEON_SECRET_5": 156, 28 | "WI_DUNGEON_SECRET_6": 157, 29 | "WI_DUNGEON_SECRET_7": 158, 30 | "WI_DUNGEON_SECRET_8": 159, 31 | "WI_DUNGEON_SECRET_9": 160, 32 | "WI_DUNGEON_SECRET_10": 161, 33 | "WI_DUNGEON_SECRET_11": 162, 34 | "WI_DUNGEON_SECRET_12": 163, 35 | "WI_DUNGEON_SECRET_13": 164, 36 | "WI_DUNGEON_SECRET_14": 165, 37 | "WI_DUNGEON_SECRET_15": 166, 38 | "WI_DUNGEON_SECRET_16": 167, 39 | "WI_DUNGEON_SECRET_L": 167, 40 | "WI_WORLD_EVENT01": 120, 41 | "WI_DUNGEON_FL_MAS": 200, 42 | "WI_DUNGEON_DA_DK": 201, 43 | "WI_WORLD_GUILDWAR": 202, 44 | "WI_DUNGEON_VOLCANE": 203, 45 | "WI_DUNGEON_SA_TA": 204, 46 | "WI_DUNGEON_SA_TA2": 205, 47 | "WI_DUNGEON_VOLCANERED": 206, 48 | "WI_DUNGEON_VOLCANEYELLOW": 207, 49 | "WI_WORLD_ARENA": 208, 50 | "WI_WORLD_MINIROOM": 209, 51 | "WI_WORLD_QUIZ": 211, 52 | "WI_GUILDHOUSE_SMALL": 212, 53 | "WI_GUILDHOUSE_MIDDLE": 213, 54 | "WI_GUILDHOUSE_LARGE": 214, 55 | "WI_DUNGEON_MUSCLE": 210, 56 | "WI_DUNGEON_KRRR": 220, 57 | "WI_DUNGEON_BEAR": 230, 58 | "WI_WORLD_GUILDWAR1TO1_0": 241, 59 | "WI_WORLD_GUILDWAR1TO1_1": 242, 60 | "WI_WORLD_GUILDWAR1TO1_2": 243, 61 | "WI_WORLD_GUILDWAR1TO1_3": 244, 62 | "WI_WORLD_GUILDWAR1TO1_L": 244 63 | } -------------------------------------------------------------------------------- /resources/definelordskill.json: -------------------------------------------------------------------------------- 1 | { 2 | "LI_CHEERING": 0, 3 | "LI_BLESSING": 1, 4 | "LI_LOVE": 2, 5 | "LI_SHOUT": 3, 6 | "LI_RAGE": 4, 7 | "LI_SUMMON": 5, 8 | "LI_TELEPORT": 6, 9 | "LI_PARTY": 7, 10 | "LT_PASSIVE": 1, 11 | "LT_ITEMBUF": 2, 12 | "LT_SUMMON": 3, 13 | "LT_TELEPORT": 4, 14 | "LTT_NA": 0, 15 | "LTT_SELF": 1, 16 | "LTT_OTHER": 2, 17 | "LTT_BOTH": 3 18 | } -------------------------------------------------------------------------------- /resources/definequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "QUEST_CHANGEJOB1": 1, 3 | "QUEST_CHANGEJOB2": 2, 4 | "QUEST_CHANGEJOB3": 3, 5 | "QUEST_CHANGEJOB4": 4, 6 | "QUEST_CHANGEJOB5": 5, 7 | "QUEST_CHANGEPUR": 6, 8 | "QUEST_1": 7, 9 | "QUEST_2": 8, 10 | "QUEST_3": 9, 11 | "QUEST_4": 10, 12 | "QUEST_5": 11, 13 | "QUEST_0": 12, 14 | "QUEST_6": 13, 15 | "QUEST_7": 14, 16 | "QUEST_8": 15, 17 | "QUEST_9": 16, 18 | "QUEST_10": 17, 19 | "QUEST_11": 18, 20 | "QUEST_12": 19, 21 | "QUEST_13": 20, 22 | "QUEST_14_VAGRANTMIND": 21, 23 | "QUEST_15": 22, 24 | "QUEST_16": 23, 25 | "QUEST_17": 24, 26 | "QUEST_18": 25, 27 | "QUEST_19": 26, 28 | "QUEST_20": 27, 29 | "QUEST_21": 28, 30 | "QUEST_22": 29, 31 | "QUEST_23": 30, 32 | "QUEST_24": 31, 33 | "QUEST_25": 32, 34 | "QUEST_26": 33, 35 | "QUEST_27": 34, 36 | "QUEST_28": 35, 37 | "QUEST_29": 36, 38 | "QUEST_30": 37, 39 | "QUEST_31": 38, 40 | "QUEST_GUILD": 39, 41 | "QUEST_WARMON_LV1ID": 40, 42 | "QUEST2_HEROSTORY": 41, 43 | "QUEST2_ACHIVEMENT1": 42, 44 | "QUEST2_ACHIVEMENT2": 43, 45 | "QUEST2_HEROWAY": 44, 46 | "QUEST2_HEROMIND": 45, 47 | "QUEST_BEGINNER": 46, 48 | "QUEST_SANTA": 47, 49 | "QUEST_MASDU1": 48, 50 | "QUEST_MASDU2": 49, 51 | "QUEST_MASDU3": 50, 52 | "QUEST_VALENTINE": 51, 53 | "QUEST_WHITEDAY": 52, 54 | "QUEST_DUDK_VOL1": 67, 55 | "QUEST_DUDK_VOL2": 68, 56 | "QUEST_DUDK_VOL3": 69, 57 | "QUEST_DUDK_VOL4": 70, 58 | "QUEST_DUDK_VOL5": 71, 59 | "QUEST_DUDK_VOL6": 72, 60 | "QUEST_DUDK_VOL7": 73, 61 | "QUEST_DUDK_VOL8": 74, 62 | "QUEST_DUDK_VOL9": 75, 63 | "QUEST_DUDK_VOL10": 76, 64 | "QUEST_DUDK_VOL11": 77, 65 | "QUEST_DUDK_VOL12": 78, 66 | "QUEST_DUDK_VOL13": 79, 67 | "QUEST_JAPEVE_BOZFL": 80, 68 | "QUEST_JAPEVE_BOZSA": 81, 69 | "QUEST_JAPEVE_BOZDA": 82, 70 | "QUEST_FIND_REDBANGT": 83, 71 | "QUEST_GETBACK_LUDASAFE": 84, 72 | "QUEST_PLOT_COLACK": 85, 73 | "QUEST_DISAPP_SCRIPT": 86, 74 | "QUEST_FIND_SCRIPT": 87, 75 | "QUEST_MANUAL_HELPER": 88, 76 | "QUEST_FIND_PORTRAIT": 89, 77 | "QUEST_CLUE1_PORTRAIT": 90, 78 | "QUEST_CLUE2_PORTRAIT": 91, 79 | "QUEST_GETBACK_PORTRAIT": 92, 80 | "QUEST_FIND_BONEWAG": 93, 81 | "QUEST_FIND_DNAWAG": 94, 82 | "QUEST_ORIGIN_BONEBOWL": 95, 83 | "QUEST_FIRST_BONEBOWL": 96, 84 | "QUEST_TRUTH_BONEBOWL": 97, 85 | "QUEST_EVE_LOSTXMAS1": 98, 86 | "QUEST_EVE_LOSTXMAS2": 99, 87 | "QUEST_EVE_LOSTXMAS3": 100, 88 | "QUEST_TEAR_MOTHER": 101, 89 | "QUEST_LOST_CHILD": 102, 90 | "QUEST_TRUTH_MIA": 103, 91 | "QUEST_DOLL_MIA": 104, 92 | "QUEST_TRUTH_PAST": 105, 93 | "QUEST_EVE_GBRCSOUP": 106, 94 | "QUEST_DREADBIG": 107, 95 | "QUEST_DREADKREN": 108, 96 | "QUEST_DREADMUTE": 109, 97 | "QUEST_REVENGE_RBANG": 110, 98 | "QUEST_RBIDENTITY": 111, 99 | "QUEST_ARCTHIEF": 112, 100 | "QUEST_SPECHILD": 113, 101 | "QUEST_SECDEAL": 114, 102 | "QUEST_MIACURSE": 115, 103 | "QUEST_LEGENDREDMANTIS1": 116, 104 | "QUEST_LEGENDREDMANTIS2": 117, 105 | "QUEST_LEGENDREDMANTIS3": 118, 106 | "QUEST_EVE_LOVEHOUSE": 119, 107 | "QUEST_EVE_LOVEHOUSE1": 120, 108 | "QUEST_EVE_LOVEHOUSE2": 121, 109 | "QUEST_RBCIVILWAR": 122, 110 | "QUEST_NEWLORDB": 123, 111 | "QUEST_LORDBAMBITION": 124, 112 | "QUEST_UNITYREDB": 125, 113 | "QUEST_NEWPETYORN": 126, 114 | "QUEST_PUMPKINCHILD": 127, 115 | "QUEST_DORIVINIG": 128, 116 | "QUEST_SUMMONDARK": 129, 117 | "QUEST_PHANTASM": 130, 118 | "QUEST_SERUSURIEL": 131, 119 | "QUEST_THEWAR": 132, 120 | "QUEST_EXISTDESIRE": 133, 121 | "QUEST_CLOCK1ST": 134, 122 | "QUEST_CLOCK2ND": 135, 123 | "QUEST_ABILITYYE": 136, 124 | "QUEST_IBLBOSS": 137, 125 | "QUEST_DARKGATE": 138, 126 | "QUEST_CLOWNTEMPLE": 139, 127 | "QUEST_EXISTDARK": 140, 128 | "QUEST_INTHEDARK": 141, 129 | "QUEST_CREGUILD": 142, 130 | "QUEST_TEACHERICINISONE": 148, 131 | "QUEST_TEACHERICINISTWO": 149, 132 | "QUEST_TEACHERICINISTHR": 150, 133 | "QUEST_VOCMER_BFTRN": 151, 134 | "QUEST_VOCMER_TRN1": 152, 135 | "QUEST_VOCMER_TRN2": 153, 136 | "QUEST_VOCMER_TRN3": 154, 137 | "QUEST_VOCASS_BFTRN": 155, 138 | "QUEST_VOCASS_TRN1": 156, 139 | "QUEST_VOCASS_TRN2": 157, 140 | "QUEST_VOCASS_TRN3": 158, 141 | "QUEST_VOCMAG_BFTRN": 159, 142 | "QUEST_VOCMAG_TRN1": 160, 143 | "QUEST_VOCMAG_TRN2": 161, 144 | "QUEST_VOCMAG_TRN3": 162, 145 | "QUEST_VOCACR_BFTRN": 53, 146 | "QUEST_VOCACR_TRN1": 54, 147 | "QUEST_VOCACR_TRN2": 55, 148 | "QUEST_VOCACR_TRN3": 56, 149 | "QUEST_HEROKNI_TRN1": 163, 150 | "QUEST_HEROKNI_TRN2": 164, 151 | "QUEST_HEROKNI_TRN3": 165, 152 | "QUEST_HEROKNI_TRN4": 166, 153 | "QUEST_HEROKNI_TRN5": 167, 154 | "QUEST_HEROBLA_TRN1": 168, 155 | "QUEST_HEROBLA_TRN2": 169, 156 | "QUEST_HEROBLA_TRN3": 170, 157 | "QUEST_HEROBLA_TRN4": 171, 158 | "QUEST_HEROBLA_TRN5": 172, 159 | "QUEST_HEROBIL_TRN1": 173, 160 | "QUEST_HEROBIL_TRN2": 174, 161 | "QUEST_HEROBIL_TRN3": 175, 162 | "QUEST_HEROBIL_TRN4": 176, 163 | "QUEST_HEROBIL_TRN5": 177, 164 | "QUEST_HERORIN_TRN1": 178, 165 | "QUEST_HERORIN_TRN2": 179, 166 | "QUEST_HERORIN_TRN3": 180, 167 | "QUEST_HERORIN_TRN4": 181, 168 | "QUEST_HERORIN_TRN5": 182, 169 | "QUEST_HEROPSY_TRN1": 183, 170 | "QUEST_HEROPSY_TRN2": 184, 171 | "QUEST_HEROPSY_TRN3": 185, 172 | "QUEST_HEROPSY_TRN4": 186, 173 | "QUEST_HEROPSY_TRN5": 187, 174 | "QUEST_HEROELE_TRN1": 188, 175 | "QUEST_HEROELE_TRN2": 189, 176 | "QUEST_HEROELE_TRN3": 190, 177 | "QUEST_HEROELE_TRN4": 191, 178 | "QUEST_HEROELE_TRN5": 192, 179 | "QUEST_HEROJES_TRN1": 57, 180 | "QUEST_HEROJES_TRN2": 58, 181 | "QUEST_HEROJES_TRN3": 59, 182 | "QUEST_HEROJES_TRN4": 60, 183 | "QUEST_HEROJES_TRN5": 61, 184 | "QUEST_HERORAN_TRN1": 62, 185 | "QUEST_HERORAN_TRN2": 63, 186 | "QUEST_HERORAN_TRN3": 64, 187 | "QUEST_HERORAN_TRN4": 65, 188 | "QUEST_HERORAN_TRN5": 66, 189 | "QUEST_SCENARIO1": 1000, 190 | "QUEST_SCENARIO1_1": 1001, 191 | "QUEST_SCENARIO1_2": 1002, 192 | "QUEST_SCENARIO1_3": 1003, 193 | "QUEST_SCENARIO2": 1004, 194 | "QUEST_SCENARIO3": 1005, 195 | "QUEST_SCENARIO3_1": 1006, 196 | "QUEST_SCENARIO3_2": 1007, 197 | "QUEST_SCENARIO3_3": 1008, 198 | "QUEST_SCENARIO3_4": 1009, 199 | "QUEST_SCENARIO3_5": 1010, 200 | "QUEST_SCENARIO3_6": 1011, 201 | "QUEST_SCENARIO4": 1012, 202 | "QUEST_SCENARIO5": 1013, 203 | "QUEST_SCENARIO5_1": 1014, 204 | "QUEST_SCENARIO5_2": 1015, 205 | "QUEST_SCENARIO5_3": 1016, 206 | "QUEST_SCE_BEGINDOUT": 1017, 207 | "QUEST_SCE_REASONCONDIV": 1018, 208 | "QUEST_SCE_PLOTBEGIN": 1019, 209 | "QUEST_SCE_BUBBLEPLN": 1020, 210 | "QUEST_SCE_STRMARK": 1021, 211 | "QUEST_SCE_VIOMAGTRP": 1022, 212 | "QUEST_SCE_SHADEHILL": 1023, 213 | "QUEST_SCE_VMTAM": 1024, 214 | "QUEST_SCE_TRBHISTORY": 1025, 215 | "QUEST_SCE_BLKMAGI": 1026, 216 | "QUEST_SCE_SCEALTAR": 1027, 217 | "QUEST_SCE_MYSTGEM": 1028, 218 | "QUEST_SCE_LAOLA": 1029, 219 | "QUEST_SCE_VMIDEN": 1030, 220 | "QUEST_SCE_SHADOW": 1031, 221 | "QUEST_SCE_DAILYBOOK": 1032, 222 | "QUEST_SCE_DAILYBOOK1": 1033, 223 | "QUEST_SCE_DAILYBOOK2": 1034, 224 | "QUEST_SCE_HARMONINFIND": 1035, 225 | "QUEST_SCE_HARMONINFIND1": 1036, 226 | "QUEST_SCE_HARMONINFIND2": 1037, 227 | "QUEST_SCE_HARMONINFIND3": 1039, 228 | "QUEST_SCE_HARMONINFIND4": 1040, 229 | "QUEST_SCE_HARMONINFIND5": 1041, 230 | "QUEST_SCE_HARMONINDAILYBOOK1": 1042, 231 | "QUEST_SCE_HARMONINDAILYBOOK2": 1043, 232 | "QUEST_SCE_HARMONINDAILYBOOK3": 1044, 233 | "QUEST_SCE_HARMONINDAILYBOOK4": 1045, 234 | "QUEST_SCE_HARMONINDAILYBOOK5": 1046, 235 | "QUEST_SCE_QUESTIONDAILY1": 1047, 236 | "QUEST_SCE_QUESTIONDAILY2": 1048, 237 | "QUEST_SCE_QUESTIONDAILY3": 1049, 238 | "QUEST_SCE_QUESTIONDAILY4": 1050, 239 | "QUEST_SCE_QUESTIONDAILY5": 1051, 240 | "QUEST_SCE_QUESTIONDAILY6": 1061, 241 | "QUEST_SCE_CAVEDISCOVERY1": 1052, 242 | "QUEST_SCE_CAVEDISCOVERY2": 1053, 243 | "QUEST_SCE_CAVEDISCOVERY3": 1054, 244 | "QUEST_SCE_CAVEDISCOVERY4": 1055, 245 | "QUEST_SCE_MDRIGALTEACHER1": 1056, 246 | "QUEST_SCE_MDRIGALTEACHER2": 1057, 247 | "QUEST_SCE_MDRIGALTEACHER3": 1058, 248 | "QUEST_SCE_MDRIGALTEACHER4": 1059, 249 | "QUEST_SCE_MDRIGALTEACHER5": 1060, 250 | "QUEST_WARMON_LV1": 1, 251 | "QUEST_BOSS_LV1": 2, 252 | "QUEST_BOSS_LV2": 3, 253 | "QUEST_BOSS_LV3": 4, 254 | "QUEST_RICECAKE1": 143, 255 | "QUEST_RICECAKE2": 144, 256 | "QUEST_RICECAKE3": 145, 257 | "QUEST_RICECAKE4": 146, 258 | "QUEST_RICECAKE5": 147, 259 | "QUEST_AUTUMNEVENT01": 5000, 260 | "QUEST_AUTUMNEVENT02": 5001, 261 | "QUEST_AUTUMNEVENT03": 5002, 262 | "QUEST_AUTUMNEVENT04": 5003, 263 | "QUEST_PKMINUS": 5004, 264 | "QUEST_TEACHER01": 5005, 265 | "QUEST_TEACHER02": 5006, 266 | "QUEST_TEACHER03": 5007, 267 | "QUEST_COOKER01": 5008, 268 | "QUEST_COOKER02": 5009, 269 | "QUEST_ARBORDAY01": 5010, 270 | "QUEST_ARBORDAY02": 5011, 271 | "QUEST_ARBORDAY03": 5012, 272 | "QUEST_ARBORDAY04": 5013, 273 | "QUEST_PANG": 5014, 274 | "QUEST_KAWIBAWIBO01": 5015, 275 | "QUEST_VOLCANETELEPORT": 5016, 276 | "QUEST_PETTAME01": 5017, 277 | "QUEST_PETTAME02": 5018, 278 | "QUEST_PETTAME03": 5019, 279 | "QUEST_PETTAME04": 5020, 280 | "QUEST_PETTAME05": 5021, 281 | "QUEST_MASTER": 5022, 282 | "QUEST_HERO": 5023, 283 | "QUEST_JULY01": 5024, 284 | "QUEST_JULY02": 5025, 285 | "QUEST_NESTLE02_1": 5026, 286 | "QUEST_NESTLE02_2": 5027, 287 | "QUEST_NESTLE02_3": 5028, 288 | "QUEST_NESTLE03": 5029, 289 | "QUEST_KAWIBAWIBO02": 5030, 290 | "QUEST_STUFF": 5031, 291 | "QUEST_SANTA01": 5032, 292 | "QUEST_SANTA02": 5033, 293 | "QUEST_COLLECTOR": 5034, 294 | "QUEST_DARKON": 5035, 295 | "QUEST_KAWIBAWIBO03": 5036, 296 | "QUEST_DARKON02": 5037, 297 | "QUEST_PREPAIDCARD": 5038, 298 | "QUEST_TUTORIAL01": 5039, 299 | "QUEST_TUTORIAL02": 5040, 300 | "QUEST_TUTORIAL03": 5041, 301 | "QUEST_TUTORIAL04": 5042, 302 | "QUEST_TUTORIAL05": 5043, 303 | "QUEST_TUTORIAL06": 5044, 304 | "QUEST_JULY03": 5045, 305 | "QUEST_JULY04": 5046, 306 | "QUEST_MINIWHEEL": 5047, 307 | "QUEST_EVENTBALL": 5048, 308 | "QUEST_HALLOWEEN01": 5049, 309 | "QUEST_HALLOWEEN02": 5050, 310 | "QUEST_HALLOWEEN03": 5051, 311 | "QUEST_SNOWMAN01": 5052, 312 | "QUEST_SNOWMAN02": 5053, 313 | "QUEST_SNOWMAN03": 5054, 314 | "QUEST_RESTATE01": 5055, 315 | "QUEST_GRAHOO01": 5056, 316 | "QUEST_VALENTINE2009": 5057, 317 | "QUEST_WHATE2009": 5058, 318 | "QUEST_ABEROO01": 5059, 319 | "QUEST_ABEROO02": 5060, 320 | "QUEST_COOK01": 5061, 321 | "QUEST_ALICE01": 5062, 322 | "QUEST_ALICE02": 5063, 323 | "QUEST_ALICE03": 5064, 324 | "QUEST_ALICE04": 5065, 325 | "QUEST_ALICE05": 5066, 326 | "QUEST_ALICE06": 5067, 327 | "QUEST_ALICE07": 5068, 328 | "QUEST_RECIPE200901": 5069, 329 | "QUEST_RECIPE200902": 5070, 330 | "QUEST_RECIPE200903": 5071, 331 | "QUEST_SEVENTH2009": 5072, 332 | "QUEST_MERON": 5073, 333 | "QUEST_AUGUSTBEER01": 5074, 334 | "QUEST_AUGUSTBEER02": 5075, 335 | "QUEST_PICKSIROSE": 5076, 336 | "QUEST_BRAVERYCHALLENGE": 5077, 337 | "QUEST_HALLOWEEN01_2009": 5078, 338 | "QUEST_HALLOWEEN02_2009": 5079, 339 | "QUEST_HALLOWEEN03_2009": 5080, 340 | "QUEST_VanHarlen01": 5081, 341 | "QUEST_VanHarlen02": 5082, 342 | "QUEST_CHRISTMASROMEO": 5083, 343 | "QUEST_CHRISTMASJULIET": 5084, 344 | "QUEST_CHRISTMAS2009_1": 5085, 345 | "QUEST_CHRISTMAS2009_2": 5086, 346 | "QUEST_SNOWMAN01_1": 5087, 347 | "QUEST_SNOWMAN02_1": 5088, 348 | "QUEST_SNOWMAN03_1": 5089, 349 | "QUEST_VALENTINE2010": 5090, 350 | "QUEST_NEWBIE1_ASS": 5091, 351 | "QUEST_NEWBIE1_MER": 5092, 352 | "QUEST_NEWBIE1_MAG": 5093, 353 | "QUEST_NEWBIE1_ACR": 5094, 354 | "QUEST_NEWBIE2_ASS": 5095, 355 | "QUEST_NEWBIE2_MER": 5096, 356 | "QUEST_NEWBIE2_MAG": 5097, 357 | "QUEST_NEWBIE2_ACR": 5098, 358 | "QUEST_NEWBIE3_BIL": 5099, 359 | "QUEST_NEWBIE3_RIN": 5100, 360 | "QUEST_NEWBIE3_BLA": 5101, 361 | "QUEST_NEWBIE3_KIN": 5102, 362 | "QUEST_NEWBIE3_ELE": 5103, 363 | "QUEST_NEWBIE3_PSY": 5104, 364 | "QUEST_NEWBIE3_JST": 5105, 365 | "QUEST_NEWBIE3_RAG": 5106, 366 | "QUEST_BUBBLEGIFT": 5107, 367 | "QUEST_LEPRECHAUN01": 5108, 368 | "QUEST_LEPRECHAUN02": 5109, 369 | "QUEST_WHITE2010": 5110, 370 | "QUEST_SKELETON01": 5111, 371 | "QUEST_KIND_SCENARIO": 6000, 372 | "QUEST_KIND_NORMAL": 6001, 373 | "QUEST_KIND_REQUEST": 6002, 374 | "QUEST_KIND_EVENT": 6003, 375 | "QS_END": 14, 376 | "QT_GENERAL": 0, 377 | "QT_REQUEST": 1, 378 | "QT_SCENARIO1": 2, 379 | "QSAY_BEGIN1": 0, 380 | "QSAY_BEGIN2": 1, 381 | "QSAY_BEGIN3": 2, 382 | "QSAY_BEGIN4": 3, 383 | "QSAY_BEGIN5": 4, 384 | "QSAY_BEGIN_YES": 5, 385 | "QSAY_BEGIN_NO": 6, 386 | "QSAY_END_COMPLETE1": 7, 387 | "QSAY_END_COMPLETE2": 8, 388 | "QSAY_END_COMPLETE3": 9, 389 | "QSAY_END_FAILURE1": 10, 390 | "QSAY_END_FAILURE2": 11, 391 | "QSAY_END_FAILURE3": 12, 392 | "QSAY_EXTRA01": 15, 393 | "QSAY_EXTRA02": 16, 394 | "QSAY_EXTRA03": 17, 395 | "QSAY_EXTRA04": 18, 396 | "QSAY_EXTRA05": 19, 397 | "QSAY_EXTRA06": 20, 398 | "QSAY_EXTRA07": 21, 399 | "QSAY_EXTRA08": 22, 400 | "QSAY_EXTRA09": 23, 401 | "QSAY_EXTRA10": 24, 402 | "QSAY_EXTRA11": 25, 403 | "QSAY_EXTRA12": 26, 404 | "QSAY_EXTRA13": 27, 405 | "QSAY_EXTRA14": 28, 406 | "QSAY_EXTRA15": 29 407 | } --------------------------------------------------------------------------------