├── .travis.yml ├── aoi ├── aoi.go ├── cell │ ├── cell_test.go │ └── cellaoi.go └── cloud │ ├── cloudaoi.go │ └── test.go ├── cmd ├── directive │ └── main.go ├── fuck │ ├── main.go │ └── main.go~ ├── hijack │ └── main.go ├── monsterinfo │ └── main.go └── test.go ├── config └── config.go ├── data ├── bloodbiblesigninfo.go ├── directivesetinfo.go ├── directivesetinfo.json ├── effectinfo.go ├── extrainfo.go ├── gearinfo.go ├── gearinfo_test.go ├── gearslotinfo.go ├── inventoryinfo.go ├── inventoryslotinfo.go ├── map.go ├── monster.json ├── monsterinfo.go ├── monsterinfo.json ├── nicknameinfo.go ├── npcinfo.go ├── pcinfo.go ├── pcinfo_test.go ├── pciteminfo.go ├── pcskillinfo.go ├── size_test.go ├── smp.go ├── smp_test.go ├── subiteminfo.go ├── zoneinfo.go └── zoneinfo.json ├── gameserver ├── agent.go ├── ai.go ├── bitset.go ├── corpse.go ├── creature.go ├── directive.go ├── effect.go ├── item.go ├── main.go ├── message.go ├── monster.go ├── npc.go ├── object.go ├── ouster.go ├── packethandler.go ├── player.go ├── playercreature.go ├── portal.go ├── scene.go ├── skill.go ├── skill2object.go ├── skill2self.go ├── skill2tile.go ├── skillformula.go ├── skillhandler.go ├── skillinfo.go ├── skillslot.go ├── slayer.go ├── tile.go ├── time.go ├── vampire.go ├── zone.go └── zoneinfo.go ├── log └── log.go ├── loginserver └── main.go ├── packet ├── api.go ├── cg.go ├── cl.go ├── gc.go ├── lc.go ├── marshal_test.go ├── packet.go └── packet_test.go └── util ├── const.go ├── type.go └── utils.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2 5 | - tip 6 | 7 | install: 8 | - go get github.com/ugorji/go/codec 9 | - go install github.com/tiancaiamao/ouster/gameserver 10 | - go install github.com/tiancaiamao/ouster/loginserver 11 | -------------------------------------------------------------------------------- /aoi/aoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | type Entity interface { 4 | X() uint8 5 | Y() uint8 6 | Id() uint32 7 | } 8 | 9 | type Callback func(watcher Entity, marker Entity) 10 | type Aoi interface { 11 | Add(x uint8, y uint8, id uint32) Entity 12 | Del(e Entity) 13 | Nearby(x uint8, y uint8, cb Callback) 14 | Update(e Entity, x uint8, y uint8) 15 | Message(cb Callback) 16 | } 17 | -------------------------------------------------------------------------------- /aoi/cell/cell_test.go: -------------------------------------------------------------------------------- 1 | package cell 2 | 3 | import ( 4 | "fmt" 5 | "github.com/tiancaiamao/ouster/aoi" 6 | "testing" 7 | ) 8 | 9 | func Test(t *testing.T) { 10 | cellaoi := New(256, 256, 32, 32) 11 | o1 := cellaoi.Add(0, 0, 1) 12 | o2 := cellaoi.Add(100, 0, 2) 13 | o3 := cellaoi.Add(0, 100, 3) 14 | cellaoi.Add(100, 100, 4) 15 | 16 | t.Log(o1.Id(), o2.Id(), o3.Id()) 17 | 18 | for i := 0; i < 100; i++ { 19 | cellaoi.Update(o1, o1.X()+1, o1.Y()+1) 20 | cellaoi.Update(o2, o2.X()-1, o2.Y()+1) 21 | cellaoi.Update(o3, o3.X()+1, o3.Y()-1) 22 | 23 | t.Logf("---------the %dth round-----------", i) 24 | cellaoi.Message(func(watcher aoi.Entity, marker aoi.Entity) { 25 | t.Logf("watcher %d (%d %d)=> marker %d (%d %d)", 26 | watcher.Id(), 27 | watcher.X(), 28 | watcher.Y(), 29 | marker.Id(), 30 | marker.X(), 31 | marker.Y()) 32 | }) 33 | } 34 | fmt.Println("finished") 35 | } 36 | -------------------------------------------------------------------------------- /aoi/cell/cellaoi.go: -------------------------------------------------------------------------------- 1 | package cell 2 | 3 | import "github.com/tiancaiamao/ouster/aoi" 4 | 5 | type Entity struct { 6 | next *Entity 7 | prev *Entity 8 | 9 | id uint32 10 | x uint8 11 | y uint8 12 | version uint8 13 | } 14 | 15 | func (e *Entity) X() uint8 { 16 | return e.x 17 | } 18 | 19 | func (e *Entity) Y() uint8 { 20 | return e.y 21 | } 22 | 23 | func (e *Entity) Id() uint32 { 24 | return e.id 25 | } 26 | 27 | func (e *Entity) remove() { 28 | e.prev.next = e.next 29 | if e.next != nil { 30 | e.next.prev = e.prev 31 | } 32 | } 33 | 34 | type Sector struct { 35 | head Entity 36 | nearby [8]*Sector 37 | } 38 | 39 | func (s *Sector) insert(e *Entity) { 40 | e.next = s.head.next 41 | if s.head.next != nil { 42 | s.head.next.prev = e 43 | } 44 | s.head.next = e 45 | e.prev = &s.head 46 | } 47 | 48 | type CellAoi struct { 49 | mapWidth uint16 50 | mapHeight uint16 51 | cellWidth uint8 52 | cellHeight uint8 53 | cells [][]Sector 54 | version uint8 55 | } 56 | 57 | var idx2dir [8][2]int = [8][2]int{ 58 | [2]int{-1, -1}, 59 | [2]int{0, -1}, 60 | [2]int{1, -1}, 61 | [2]int{1, 0}, 62 | [2]int{1, 1}, 63 | [2]int{0, 1}, 64 | [2]int{-1, 1}, 65 | [2]int{-1, 0}, 66 | } 67 | 68 | func New(mapWidth, mapHeight uint16, cellWidth, cellHeight uint8) *CellAoi { 69 | ret := &CellAoi{ 70 | mapWidth: mapWidth, 71 | mapHeight: mapHeight, 72 | cellWidth: cellWidth, 73 | cellHeight: cellHeight, 74 | } 75 | 76 | width := (mapWidth+uint16(cellWidth))/uint16(cellWidth) - 1 77 | height := (mapHeight+uint16(cellHeight))/uint16(cellHeight) - 1 78 | ret.cells = make([][]Sector, width) 79 | for i := 0; i < int(width); i++ { 80 | ret.cells[i] = make([]Sector, height) 81 | } 82 | 83 | for x := 0; x < int(width); x++ { 84 | for y := 0; y < int(height); y++ { 85 | sector := &ret.cells[x][y] 86 | 87 | for i := 0; i < 8; i++ { 88 | dir := idx2dir[i] 89 | xx := x + dir[0] 90 | yy := y + dir[1] 91 | 92 | if xx >= 0 && xx < int(width) && yy >= 0 && yy < int(height) { 93 | sector.nearby[i] = &ret.cells[xx][yy] 94 | } 95 | } 96 | } 97 | } 98 | 99 | return ret 100 | } 101 | 102 | func (aoi *CellAoi) getCell(x uint8, y uint8) *Sector { 103 | xIndex := x / aoi.cellWidth 104 | yIndex := y / aoi.cellHeight 105 | 106 | return &aoi.cells[xIndex][yIndex] 107 | } 108 | 109 | func (aoi *CellAoi) Add(x uint8, y uint8, id uint32) aoi.Entity { 110 | sector := aoi.getCell(x, y) 111 | if sector == nil { 112 | return nil 113 | } 114 | 115 | // TODO: object pool to reuse memory 116 | ret := new(Entity) 117 | ret.x = x 118 | ret.y = y 119 | ret.id = id 120 | sector.insert(ret) 121 | return ret 122 | } 123 | 124 | func (aoi *CellAoi) Del(entity aoi.Entity) { 125 | e := entity.(*Entity) 126 | e.remove() 127 | } 128 | 129 | func (aoi *CellAoi) Message(cb aoi.Callback) { 130 | aoi.version++ 131 | 132 | width := (aoi.mapWidth+uint16(aoi.cellWidth))/uint16(aoi.cellWidth) - 1 133 | height := (aoi.mapHeight+uint16(aoi.cellHeight))/uint16(aoi.cellHeight) - 1 134 | for x := 0; x < int(width); x++ { 135 | for y := 0; y < int(height); y++ { 136 | sector := &aoi.cells[x][y] 137 | 138 | for e := sector.head.next; e != nil; e = e.next { 139 | if e.version == aoi.version { 140 | for ee := sector.head.next; ee != nil; ee = ee.next { 141 | if e != ee { 142 | cb(e, ee) 143 | } 144 | } 145 | } else { 146 | e.version = aoi.version 147 | } 148 | } 149 | } 150 | } 151 | } 152 | 153 | func (aoi *CellAoi) Update(entity aoi.Entity, x uint8, y uint8) { 154 | e := entity.(*Entity) 155 | e.version++ 156 | oldSector := aoi.getCell(e.x, e.y) 157 | newSector := aoi.getCell(x, y) 158 | 159 | // remove from oldSector and insert into newSector 160 | if oldSector != newSector { 161 | e.remove() 162 | newSector.insert(e) 163 | } 164 | 165 | e.x = x 166 | e.y = y 167 | } 168 | 169 | func (aoi *CellAoi) Nearby(x uint8, y uint8, cb aoi.Callback) { 170 | sector := aoi.getCell(x, y) 171 | 172 | for e := sector.head.next; e != nil; e = e.next { 173 | cb(nil, e) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /aoi/cloud/cloudaoi.go: -------------------------------------------------------------------------------- 1 | // 解决的根本问题就是,哪个点,周围有哪些东西。啊个点有哪些东西,最好由player维护。 2 | // 这个接口是只能由Map调的,不能用Player直接掉。 3 | // 间接的思路是,不维护东西链表。 4 | package cloud 5 | 6 | // import "fmt" 7 | 8 | type FPoint struct { 9 | X float32 10 | Y float32 11 | } 12 | 13 | type object struct { 14 | // id uint32 15 | mode AoiMode 16 | pos FPoint 17 | last FPoint 18 | version uint32 19 | } 20 | 21 | type key struct { 22 | smaller uint32 23 | larger uint32 24 | } 25 | 26 | type value struct { 27 | obj1 *object 28 | obj2 *object 29 | } 30 | 31 | type kv struct { 32 | id uint32 33 | obj *object 34 | } 35 | 36 | type Aoi struct { 37 | all map[uint32]*object 38 | move []kv 39 | change []kv 40 | version uint32 41 | hotpair map[key]value 42 | } 43 | 44 | func New() *Aoi { 45 | aoi := new(Aoi) 46 | aoi.all = make(map[uint32]*object) 47 | aoi.move = make([]kv, 0, 100) 48 | aoi.change = make([]kv, 0, 100) 49 | aoi.hotpair = make(map[key]value) 50 | return aoi 51 | } 52 | 53 | func (aoi *Aoi) Update(id uint32, mode AoiMode, pos FPoint) { 54 | if (mode & ModeDrop) != 0 { 55 | delete(aoi.all, id) 56 | return 57 | } 58 | 59 | obj, ok := aoi.all[id] 60 | if !ok { 61 | obj = new(object) 62 | aoi.all[id] = obj 63 | } 64 | 65 | changed := modeChanged(obj, mode) 66 | if changed { 67 | aoi.change = append(aoi.change, kv{id, obj}) 68 | obj.mode = mode 69 | obj.last = pos 70 | } else { 71 | moved := posChanged(obj, pos) 72 | if moved { 73 | // fmt.Printf("object moved:%d (%f %f)\n", id, pos.X, pos.Y) 74 | aoi.move = append(aoi.move, kv{id, obj}) 75 | obj.last = pos 76 | } 77 | } 78 | obj.pos = pos 79 | obj.version = aoi.version 80 | } 81 | 82 | func posChanged(obj *object, pos FPoint) bool { 83 | var ret bool 84 | if 4*distance2(obj.last, pos) >= RADIS*RADIS { 85 | ret = true 86 | } 87 | return ret 88 | } 89 | 90 | func (aoi *Aoi) addHotPair(id1 uint32, obj1 *object, id2 uint32, obj2 *object) { 91 | if id1 < id2 { 92 | aoi.hotpair[key{id1, id2}] = value{obj1, obj2} 93 | } else if id1 > id2 { 94 | aoi.hotpair[key{id2, id1}] = value{obj2, obj1} 95 | } 96 | } 97 | 98 | type AoiCallback func(uint32, uint32) 99 | 100 | func send(id1 uint32, obj1 *object, id2 uint32, obj2 *object, cb AoiCallback) { 101 | if (obj1.mode&ModeWatcher) != 0 && (obj2.mode&ModeMarker) != 0 { 102 | cb(id1, id2) 103 | } 104 | if (obj2.mode&ModeWatcher) != 0 && (obj1.mode&ModeMarker) != 0 { 105 | cb(id2, id1) 106 | } 107 | } 108 | 109 | func (aoi *Aoi) Message(cb AoiCallback) { 110 | // fmt.Println("enter Message-------------") 111 | // fmt.Println("move:", aoi.move) 112 | // fmt.Println("change:", aoi.change) 113 | 114 | // compare change and all to generate hotpair and send message 115 | for _, kv := range aoi.change { 116 | for id, obj := range aoi.all { 117 | if kv.id != id { 118 | dist := distance2(kv.obj.pos, obj.pos) 119 | if dist <= RADIS*RADIS { 120 | send(kv.id, kv.obj, id, obj, cb) 121 | } else if dist > RADIS*RADIS && dist < 4*RADIS*RADIS { 122 | aoi.addHotPair(kv.id, kv.obj, id, obj) 123 | } 124 | } 125 | } 126 | } 127 | 128 | // compare move and all to generate hotpair 129 | for _, kv := range aoi.move { 130 | for id, obj := range aoi.all { 131 | if kv.id != id { 132 | dist := distance2(kv.obj.pos, obj.pos) 133 | if dist > RADIS*RADIS && dist < 4*RADIS*RADIS { 134 | aoi.addHotPair(kv.id, kv.obj, id, obj) 135 | } 136 | } 137 | } 138 | } 139 | 140 | // fmt.Println("hotpair:", aoi.hotpair) 141 | 142 | for k, v := range aoi.hotpair { 143 | // filter those changed in update function 144 | if v.obj1.version == aoi.version || v.obj2.version == aoi.version { 145 | if distance2(v.obj1.pos, v.obj2.pos) < RADIS*RADIS { 146 | send(k.smaller, v.obj1, k.larger, v.obj2, cb) 147 | delete(aoi.hotpair, k) 148 | } 149 | } 150 | } 151 | 152 | aoi.move = aoi.move[:0] 153 | aoi.change = aoi.change[:0] 154 | aoi.version++ 155 | } 156 | -------------------------------------------------------------------------------- /aoi/cloud/test.go: -------------------------------------------------------------------------------- 1 | package cloud 2 | 3 | import "fmt" 4 | 5 | type AoiMode uint8 6 | 7 | const ( 8 | ModeWatcher = (1 << iota) 9 | ModeMarker 10 | ModeDrop 11 | ) 12 | 13 | type MyAoi struct { 14 | all map[uint32]*object 15 | watcherStatic map[uint32]*object 16 | markerStatic map[uint32]*object 17 | watcherMove map[uint32]*object 18 | markerMove map[uint32]*object 19 | // moveQueue []uint32 20 | version uint32 21 | } 22 | 23 | func NewMy() *MyAoi { 24 | aoi := new(MyAoi) 25 | aoi.all = make(map[uint32]*object) 26 | aoi.watcherStatic = make(map[uint32]*object) 27 | aoi.markerStatic = make(map[uint32]*object) 28 | aoi.watcherMove = make(map[uint32]*object) 29 | aoi.markerMove = make(map[uint32]*object) 30 | // aoi.moveQueue = make([]uint32, 0, 100) 31 | return aoi 32 | } 33 | 34 | const RADIS float32 = 20.0 35 | 36 | func (aoi *MyAoi) Update(id uint32, mode AoiMode, pos FPoint) { 37 | if (mode & ModeDrop) != 0 { 38 | delete(aoi.all, id) 39 | delete(aoi.watcherStatic, id) 40 | delete(aoi.watcherMove, id) 41 | delete(aoi.markerStatic, id) 42 | delete(aoi.markerMove, id) 43 | return 44 | } 45 | 46 | obj, ok := aoi.all[id] 47 | if !ok { 48 | obj = new(object) 49 | aoi.all[id] = obj 50 | } 51 | 52 | moved := posChanged(obj, pos) 53 | changed := modeChanged(obj, mode) 54 | if changed { 55 | obj.mode = mode 56 | moved = true 57 | } 58 | 59 | if moved { 60 | fmt.Printf("moved: %d, pos=%f, %f\n", id, obj.pos.X, obj.pos.Y) 61 | obj.pos = pos 62 | obj.last = pos 63 | obj.version = aoi.version 64 | 65 | // aoi.moveQueue = append(aoi.moveQueue, id) 66 | 67 | if (obj.mode & ModeWatcher) != 0 { 68 | delete(aoi.watcherStatic, id) 69 | aoi.watcherMove[id] = obj 70 | } 71 | if (obj.mode & ModeMarker) != 0 { 72 | delete(aoi.markerStatic, id) 73 | aoi.markerMove[id] = obj 74 | } 75 | } 76 | } 77 | 78 | func modeChanged(obj *object, mode AoiMode) bool { 79 | var changed bool 80 | if obj.mode != mode { 81 | changed = true 82 | } 83 | return changed 84 | } 85 | 86 | func (aoi *MyAoi) Message(cb AoiCallback) { 87 | fmt.Println("enter Message-------------") 88 | fmt.Println("watchMove:", aoi.watcherMove) 89 | fmt.Println("markerMove", aoi.markerMove) 90 | fmt.Println("watcherStatic", aoi.watcherStatic) 91 | fmt.Println("markerStatic", aoi.markerStatic) 92 | 93 | // delete those NOT MOVED from move set and add them to static set 94 | for id, obj := range aoi.watcherMove { 95 | if obj.version != aoi.version { 96 | aoi.watcherStatic[id] = obj 97 | delete(aoi.watcherMove, id) 98 | // fmt.Println("// delete those NOT MOVED from move set and add them to static set") 99 | } 100 | } 101 | for id, obj := range aoi.markerMove { 102 | if obj.version != aoi.version { 103 | aoi.markerStatic[id] = obj 104 | delete(aoi.markerMove, id) 105 | } 106 | } 107 | 108 | fmt.Println("after process-------------") 109 | fmt.Println("watchMove:", aoi.watcherMove) 110 | fmt.Println("markerMove", aoi.markerMove) 111 | fmt.Println("watcherStatic", aoi.watcherStatic) 112 | fmt.Println("markerStatic", aoi.markerStatic) 113 | 114 | // compare watcherMove and all marker 115 | for watcherId, watcherObj := range aoi.watcherMove { 116 | for markerId, markerObj := range aoi.markerMove { 117 | if watcherObj != markerObj { 118 | dist := distance2(watcherObj.pos, markerObj.pos) 119 | fmt.Printf("watcherMove:%d -- markerMove:%d, dist=%f\n", watcherId, markerId, dist) 120 | if dist < RADIS*RADIS { 121 | cb(watcherId, markerId) 122 | } 123 | } 124 | } 125 | for markerId, markerObj := range aoi.markerStatic { 126 | if watcherObj != markerObj { 127 | dist := distance2(watcherObj.pos, markerObj.pos) 128 | fmt.Printf("watcherMove:%d -- markerStatic:%d, dist=%f\n", watcherId, markerId, dist) 129 | 130 | if dist < RADIS*RADIS { 131 | cb(watcherId, markerId) 132 | } 133 | } 134 | } 135 | } 136 | 137 | // compare static watcher to markerMove 138 | for watcherId, watcherObj := range aoi.watcherStatic { 139 | for markerId, markerObj := range aoi.markerMove { 140 | if watcherObj != markerObj { 141 | dist := distance2(watcherObj.pos, markerObj.pos) 142 | fmt.Printf("watcherStatic:%d -- markerMove:%d, dist=%f\n", watcherId, markerId, dist) 143 | 144 | if dist < RADIS*RADIS { 145 | cb(watcherId, markerId) 146 | } 147 | } 148 | } 149 | } 150 | 151 | // aoi.moveQueue = aoi.moveQueue[:0] 152 | aoi.version++ 153 | } 154 | 155 | func distance2(p1 FPoint, p2 FPoint) float32 { 156 | return (p2.X-p1.X)*(p2.X-p1.X) + (p2.Y-p1.Y)*(p2.Y-p1.Y) 157 | } 158 | -------------------------------------------------------------------------------- /cmd/fuck/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "github.com/tiancaiamao/ouster/packet" 8 | "io" 9 | "net" 10 | ) 11 | 12 | const ( 13 | trueLoginServer = "60.169.77.55:9999" 14 | trueGameServer = "60.169.77.55:9998" 15 | ) 16 | 17 | var MAX int = 5 18 | 19 | func main() { 20 | ln, err := net.Listen("tcp", ":9999") 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | gn, err := net.Listen("tcp", ":9998") 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | notice := make(chan struct{}) 31 | go func() { 32 | client, err := ln.Accept() 33 | if err != nil { 34 | panic(err) 35 | } 36 | hajackLoginServer(client, notice) 37 | }() 38 | 39 | client, err := gn.Accept() 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | hajackGameServer(client, notice) 45 | } 46 | 47 | func hajackLoginServer(client net.Conn, notice chan<- struct{}) { 48 | server, err := net.Dial("tcp", trueLoginServer) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | go func() { 54 | var raw packet.RawPacket 55 | var buf [400]byte 56 | for { 57 | err := readRaw(client, &raw, buf[:]) 58 | if err != nil { 59 | if err == io.EOF { 60 | fmt.Println("CL客户端关了,没法读取了") 61 | return 62 | } else { 63 | panic(err) 64 | } 65 | } 66 | 67 | fmt.Println("[C->L]", len(raw.Data), raw) 68 | 69 | err = writeRaw(server, &raw) 70 | if err != nil { 71 | if err == io.EOF { 72 | fmt.Println("LC服务端关了,没法把客户端数据写过去") 73 | return 74 | } else { 75 | panic(err) 76 | } 77 | } 78 | 79 | } 80 | }() 81 | 82 | var raw packet.RawPacket 83 | var buf [400]byte 84 | for { 85 | err := readRaw(server, &raw, buf[:]) 86 | if err != nil { 87 | if err == io.EOF { 88 | fmt.Println("LC服务器那边关了,没法读取了") 89 | return 90 | } else { 91 | panic(err) 92 | } 93 | } 94 | 95 | fmt.Println("[L->C]", len(raw.Data), raw) 96 | 97 | // if raw.Id == packet.PACKET_LC_PC_LIST { 98 | // fmt.Println("run here...") 99 | // pkt := &packet.LCPCListPacket{} 100 | 101 | // raw.Data, _ = pkt.MarshalBinary(0) 102 | // fmt.Println("[实际发送L->C]", len(raw.Data), raw) 103 | // } 104 | 105 | // 劫持修改最后的服务器包 106 | if raw.Id == packet.PACKET_LC_RECONNECT { 107 | pkt, err := raw.Unmarshal(0) 108 | if err != nil { 109 | panic(err) 110 | } 111 | reconn := pkt.(*packet.LCReconnectPacket) 112 | reconn.Ip = "192.168.10.102" 113 | reconn.Port = 9998 114 | 115 | raw.Data, err = reconn.MarshalBinary(0) 116 | err = writeRaw(client, &raw) 117 | close(notice) 118 | client.Close() 119 | return 120 | } 121 | 122 | err = writeRaw(client, &raw) 123 | if err != nil { 124 | if err == io.EOF { 125 | fmt.Println("CL客户端关了,写不到客户端了") 126 | } else { 127 | panic(err) 128 | } 129 | } 130 | } 131 | } 132 | 133 | func hajackGameServer(client net.Conn, notice <-chan struct{}) { 134 | <-notice 135 | fmt.Println("收到客户端向GameServer连接请求...") 136 | server, err := net.Dial("tcp", trueGameServer) 137 | if err != nil { 138 | panic(err) 139 | } 140 | 141 | go func() { 142 | var raw packet.RawPacket 143 | var buf [800]byte 144 | for { 145 | err := readRaw(client, &raw, buf[:]) 146 | if err != nil { 147 | if err == io.EOF { 148 | fmt.Println("CG客户端关了,没法读取了") 149 | } else { 150 | panic(err) 151 | } 152 | } 153 | 154 | fmt.Println("[C->G]", raw) 155 | 156 | err = writeRaw(server, &raw) 157 | if err != nil { 158 | if err == io.EOF { 159 | fmt.Println("CG服务端关了,没法把客户端数据写过去") 160 | } else { 161 | panic(err) 162 | } 163 | } 164 | } 165 | }() 166 | 167 | var raw packet.RawPacket 168 | var buf [800]byte 169 | // var correct uint8 170 | for { 171 | err := readRaw(server, &raw, buf[:]) 172 | if err != nil { 173 | if err == io.EOF { 174 | fmt.Println("CG服务器那边关了,没法读取了") 175 | } else { 176 | panic(err) 177 | } 178 | } 179 | 180 | fmt.Println("[G->C]", raw) 181 | 182 | err = writeRaw(client, &raw) 183 | if err != nil { 184 | panic(err) 185 | } 186 | 187 | // if raw.Id == packet.PACKET_CG_SKILL_TO_OBJECT { 188 | // pkt, err := raw.Unmarshal(0) 189 | // skill := pkt.(packet.CGSkillToObjectPacket) 190 | 191 | // // 冰矛多倍 192 | // if skill.SkillType == 285 { 193 | // for i := 0; i < MAX; i++ { 194 | // correct++ 195 | // raw.Seq += correct 196 | // err = writeRaw.Write(client, raw) 197 | // if err != nil { 198 | // panic(err) 199 | // } 200 | // } 201 | // } 202 | // } 203 | } 204 | } 205 | 206 | func writeRaw(writer io.Writer, raw *packet.RawPacket) error { 207 | err := binary.Write(writer, binary.LittleEndian, raw.Id) 208 | if err != nil { 209 | return err 210 | } 211 | 212 | err = binary.Write(writer, binary.LittleEndian, uint32(len(raw.Data))) 213 | if err != nil { 214 | return err 215 | } 216 | 217 | err = binary.Write(writer, binary.LittleEndian, raw.Seq) 218 | if err != nil { 219 | return err 220 | } 221 | 222 | _, err = writer.Write(raw.Data) 223 | if err != nil { 224 | return err 225 | } 226 | 227 | return nil 228 | } 229 | 230 | func readRaw(reader io.Reader, raw *packet.RawPacket, buf []byte) (err error) { 231 | var sz uint32 232 | 233 | err = binary.Read(reader, binary.LittleEndian, &raw.Id) 234 | if err != nil { 235 | return 236 | } 237 | 238 | err = binary.Read(reader, binary.LittleEndian, &sz) 239 | if err != nil { 240 | return 241 | } 242 | 243 | err = binary.Read(reader, binary.LittleEndian, &raw.Seq) 244 | if err != nil { 245 | return 246 | } 247 | 248 | if sz > uint32(len(buf)) { 249 | return errors.New("packet size too large") 250 | } 251 | 252 | n, err := io.ReadFull(reader, buf[:sz]) 253 | if err != nil { 254 | return 255 | } 256 | if n != int(sz) { 257 | err = errors.New("read get less data than needed") 258 | return 259 | } 260 | 261 | raw.Data = buf[:sz] 262 | return nil 263 | } 264 | -------------------------------------------------------------------------------- /cmd/fuck/main.go~: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/tiancaiamao/ouster/packet" 6 | "io" 7 | "net" 8 | ) 9 | 10 | const ( 11 | trueLoginServer = "60.169.77.55:9999" 12 | trueGameServer = "60.169.77.55" 13 | ) 14 | 15 | func main() { 16 | ln, err := net.Listen("tcp", ":9999") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | gn, err := net.Listen("tcp", ":9998") 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | go func() { 27 | client, err := ln.Accept() 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | server, err := net.Dial("tcp", trueLoginServer) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | hajackLoginServer(client, server) 38 | }() 39 | 40 | client, err := gn.Accept() 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | fmt.Println("收到客户端向GameServer连接请求...") 46 | server, err := net.Dial("tcp", trueGameServer+":9998") 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | hajackGameServer(client, server) 52 | } 53 | 54 | func hajackLoginServer(client, server net.Conn) { 55 | go func() { 56 | clientReader := packet.NewReader() 57 | serverWriter := packet.NewWriter() 58 | for { 59 | pkt, err := clientReader.Read(client) 60 | if err != nil { 61 | if _, ok := err.(packet.NotImplementError); !ok { 62 | if err == io.EOF { 63 | fmt.Println("CG客户端关了,没法读取了") 64 | } else { 65 | panic(err) 66 | } 67 | } 68 | } 69 | 70 | fmt.Printf("[C->L %d] %v\n", clientReader. , pkt) 71 | 72 | // 劫持修改最后的服务器包 73 | if pkt.PacketID() == packet.PACKET_LC_RECONNECT { 74 | raw := pkt.(*packet.LCReconnectPacket) 75 | raw.Ip = trueGameServer 76 | raw.Port = 9998 77 | 78 | serverWriter.Write(server, raw) 79 | client.Close() 80 | return 81 | } 82 | 83 | err = serverWriter.Write(server, pkt) 84 | if err != nil { 85 | if err == io.EOF { 86 | fmt.Println("CG服务端关了,没法把客户端数据写过去") 87 | } else { 88 | panic(err) 89 | } 90 | } 91 | 92 | } 93 | }() 94 | 95 | serverReader := packet.NewReader() 96 | clientWriter := packet.NewWriter() 97 | for { 98 | pkt, err := serverReader.Read(server) 99 | if err != nil { 100 | if _, ok := err.(packet.NotImplementError); !ok { 101 | if err == io.EOF { 102 | fmt.Println("CG服务器那边关了,没法读取了") 103 | } else { 104 | panic(err) 105 | } 106 | } 107 | } 108 | 109 | fmt.Println("[L->C]", pkt) 110 | 111 | err = clientWriter.Write(client, pkt) 112 | if err != nil { 113 | if err == io.EOF { 114 | fmt.Println("CG客户端关了,写不到客户端了") 115 | } else { 116 | panic(err) 117 | } 118 | } 119 | } 120 | } 121 | 122 | func hajackGameServer(client, server net.Conn) { 123 | go func() { 124 | clientReader := packet.NewReader() 125 | serverWriter := packet.NewWriter() 126 | for { 127 | pkt, err := clientReader.Read(client) 128 | if err != nil { 129 | if _, ok := err.(packet.NotImplementError); !ok { 130 | if err == io.EOF { 131 | fmt.Println("CG客户端关了,没法读取了") 132 | } else { 133 | panic(err) 134 | } 135 | } 136 | } 137 | 138 | fmt.Println("[C->G]", pkt) 139 | 140 | err = serverWriter.Write(server, pkt) 141 | if err != nil { 142 | if err == io.EOF { 143 | fmt.Println("CG服务端关了,没法把客户端数据写过去") 144 | } else { 145 | panic(err) 146 | } 147 | } 148 | } 149 | }() 150 | 151 | serverReader := packet.NewReader() 152 | clientWriter := packet.NewWriter() 153 | for { 154 | pkt, err := serverReader.Read(server) 155 | if err != nil { 156 | if _, ok := err.(packet.NotImplementError); !ok { 157 | if err == io.EOF { 158 | fmt.Println("CG服务器那边关了,没法读取了") 159 | } else { 160 | panic(err) 161 | } 162 | } 163 | } 164 | 165 | fmt.Println("[G->C]", pkt) 166 | 167 | err = clientWriter.Write(client, pkt) 168 | if err != nil { 169 | if err == io.EOF { 170 | fmt.Println("CG客户端关了,写不到客户端了") 171 | } else { 172 | panic(err) 173 | } 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /cmd/hijack/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "github.com/tiancaiamao/ouster/packet" 8 | "io" 9 | "net" 10 | ) 11 | 12 | const ( 13 | trueLoginServer = "60.169.77.55:9999" 14 | trueGameServer = "60.169.77.55:9998" 15 | ) 16 | 17 | func main() { 18 | ln, err := net.Listen("tcp", ":9999") 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | gn, err := net.Listen("tcp", ":9998") 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | go func() { 29 | client, err := ln.Accept() 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | server, err := net.Dial("tcp", trueLoginServer) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | hajackLoginServer(client, server) 40 | }() 41 | 42 | client, err := gn.Accept() 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | fmt.Println("收到客户端向GameServer连接请求...") 48 | server, err := net.Dial("tcp", trueGameServer) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | hajackGameServer(client, server) 54 | } 55 | 56 | func hajackLoginServer(client, server net.Conn) { 57 | clientBuf := make([]byte, 200) 58 | serverBuf := make([]byte, 200) 59 | 60 | flag := true 61 | for flag { 62 | // read from client 63 | n, err := client.Read(clientBuf) 64 | if err != nil { 65 | if err == io.EOF { 66 | fmt.Println("CL客户端关了,没法读取了") 67 | } else { 68 | panic(err) 69 | } 70 | } 71 | 72 | fmt.Println("[C->L]", clientBuf[:n]) 73 | 74 | if n > 0 { 75 | // forward the msg to server 76 | _, err = server.Write(clientBuf[:n]) 77 | if err != nil { 78 | if err == io.EOF { 79 | fmt.Println("CL服务端关了,没法把客户端数据写过去") 80 | } else { 81 | panic(err) 82 | } 83 | } 84 | } 85 | 86 | // read from server 87 | n, err = server.Read(serverBuf) 88 | if err != nil { 89 | if err == io.EOF { 90 | flag = false 91 | fmt.Println("CL服务器那边关了,没法读取了") 92 | } else { 93 | panic(err) 94 | } 95 | } 96 | 97 | fmt.Println("[L->C]", serverBuf[:n]) 98 | 99 | if bytes.HasPrefix(serverBuf[:n], []byte{194, 1}) { 100 | // [194 1 22 0 0 0 5 13 49 57 50 46 49 54 56 46 49 46 49 50 51 14 39 0 0 0 32 6 11] 101 | key := serverBuf[n-6 : n] 102 | client.Write([]byte{194, 1, 22, 0, 0, 0, 5, 13, '1', '9', '2', '.', '1', '6', '8', '.', '1', '.', '1', '0', '2', 14, 39, key[0], key[1], key[2], key[3], key[4], key[5]}) 103 | client.Close() 104 | return 105 | } 106 | 107 | // write the msg to client 108 | if n > 0 { 109 | _, err = client.Write(serverBuf[:n]) 110 | if err != nil { 111 | if err == io.EOF { 112 | flag = false 113 | fmt.Println("客户端关了,写不到客户端了") 114 | } else { 115 | panic(err) 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | func writeRaw(writer io.Writer, raw *packet.RawPacket) error { 123 | err := binary.Write(writer, binary.LittleEndian, raw.Id) 124 | if err != nil { 125 | return err 126 | } 127 | 128 | err = binary.Write(writer, binary.LittleEndian, uint32(len(raw.Data))) 129 | if err != nil { 130 | return err 131 | } 132 | 133 | err = binary.Write(writer, binary.LittleEndian, raw.Seq) 134 | if err != nil { 135 | return err 136 | } 137 | 138 | _, err = writer.Write(raw.Data) 139 | if err != nil { 140 | return err 141 | } 142 | 143 | return nil 144 | } 145 | 146 | func hajackGameServer(client, server net.Conn) { 147 | clientBuf := make([]byte, 200) 148 | serverBuf := make([]byte, 200) 149 | 150 | go func() { 151 | for { 152 | // read from client 153 | n, err := client.Read(clientBuf) 154 | if err != nil { 155 | if err == io.EOF { 156 | fmt.Println("CG客户端关了,没法读取了") 157 | } else { 158 | panic(err) 159 | } 160 | } 161 | 162 | fmt.Println("[C->G]", clientBuf[:n]) 163 | 164 | if n > 0 { 165 | // forward the msg to server 166 | _, err = server.Write(clientBuf[:n]) 167 | if err != nil { 168 | if err == io.EOF { 169 | fmt.Println("CG服务端关了,没法把客户端数据写过去") 170 | } else { 171 | panic(err) 172 | } 173 | } 174 | } 175 | } 176 | 177 | }() 178 | 179 | flag := true 180 | for flag { 181 | // read from server 182 | n, err := server.Read(serverBuf) 183 | if err != nil { 184 | if err == io.EOF { 185 | flag = false 186 | fmt.Println("CG服务器那边关了,没法读取了") 187 | } else { 188 | panic(err) 189 | } 190 | } 191 | 192 | fmt.Println("[G->C]", serverBuf[:n]) 193 | 194 | // write the msg to client 195 | if n > 0 { 196 | _, err = client.Write(serverBuf[:n]) 197 | if err != nil { 198 | if err == io.EOF { 199 | flag = false 200 | fmt.Println("CG客户端关了,写不到客户端了") 201 | } else { 202 | panic(err) 203 | } 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /cmd/monsterinfo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | // "fmt" 6 | "github.com/tiancaiamao/ouster/data" 7 | . "github.com/tiancaiamao/ouster/util" 8 | "os" 9 | "strconv" 10 | ) 11 | 12 | type Item struct { 13 | MType string 14 | SType string 15 | EName string 16 | Level string 17 | STR string 18 | DEX string 19 | INTE string 20 | BSize string 21 | Fame string 22 | Exp string 23 | MColor string 24 | SColor string 25 | Align string 26 | AOrder string 27 | Moral string 28 | Delay string 29 | Sight string 30 | MeleeRange string 31 | MissileRange string 32 | RegenPortal string 33 | RegenInvisible string 34 | RegenBat string 35 | UnburrowChance string 36 | MMode string 37 | AIType string 38 | Master string 39 | ClanType string 40 | Chief string 41 | NormalRegin string 42 | HasTreasure string 43 | MonsterClass string 44 | SkullType string 45 | } 46 | 47 | func main() { 48 | file, err := os.Open("MonsterInfo.json") 49 | if err != nil { 50 | panic(err) 51 | } 52 | defer file.Close() 53 | 54 | var items []Item 55 | dec := json.NewDecoder(file) 56 | err = dec.Decode(&items) 57 | if err != nil { 58 | panic(err) 59 | } 60 | 61 | infos := make([]data.MonsterInfo, len(items)) 62 | for i := 0; i < len(items); i++ { 63 | item := &items[i] 64 | info := &infos[i] 65 | 66 | var tmp int 67 | tmp, _ = strconv.Atoi(item.MType) 68 | info.MonsterType = MonsterType_t(tmp) 69 | 70 | info.Name = item.EName 71 | 72 | tmp, _ = strconv.Atoi(item.Level) 73 | info.Level = Level_t(tmp) 74 | 75 | tmp, _ = strconv.Atoi(item.STR) 76 | info.STR = Attr_t(tmp) 77 | 78 | tmp, _ = strconv.Atoi(item.DEX) 79 | info.DEX = Attr_t(tmp) 80 | 81 | tmp, _ = strconv.Atoi(item.INTE) 82 | info.INTE = Attr_t(tmp) 83 | 84 | tmp, _ = strconv.Atoi(item.BSize) 85 | info.BodySize = tmp 86 | 87 | tmp, _ = strconv.Atoi(item.Exp) 88 | info.Exp = Exp_t(tmp) 89 | 90 | tmp, _ = strconv.Atoi(item.MColor) 91 | info.MColor = Color_t(tmp) 92 | 93 | tmp, _ = strconv.Atoi(item.SColor) 94 | info.SColor = Color_t(tmp) 95 | 96 | tmp, _ = strconv.Atoi(item.Sight) 97 | info.Sight = Sight_t(tmp) 98 | 99 | switch item.MMode { 100 | case "WALK": 101 | info.MoveMode = MOVE_MODE_WALKING 102 | case "FLY": 103 | info.MoveMode = MOVE_MODE_FLYING 104 | } 105 | 106 | tmp, _ = strconv.Atoi(item.MeleeRange) 107 | info.MeleeRange = tmp 108 | 109 | tmp, _ = strconv.Atoi(item.MissileRange) 110 | info.MissileRange = tmp 111 | 112 | tmp, _ = strconv.Atoi(item.AIType) 113 | info.AIType = tmp 114 | 115 | tmp, _ = strconv.Atoi(item.UnburrowChance) 116 | info.UnburrowChance = tmp 117 | 118 | tmp, _ = strconv.Atoi(item.SType) 119 | info.SType = tmp 120 | 121 | tmp, _ = strconv.Atoi(item.Fame) 122 | info.Fame = Fame_t(tmp) 123 | 124 | tmp, _ = strconv.Atoi(item.Align) 125 | info.Align = tmp 126 | 127 | tmp, _ = strconv.Atoi(item.AOrder) 128 | info.AOrder = tmp 129 | 130 | tmp, _ = strconv.Atoi(item.Moral) 131 | info.Moral = tmp 132 | 133 | tmp, _ = strconv.Atoi(item.Delay) 134 | info.Delay = tmp 135 | 136 | tmp, _ = strconv.Atoi(item.RegenPortal) 137 | info.RegenPortal = (tmp != 0) 138 | 139 | tmp, _ = strconv.Atoi(item.RegenInvisible) 140 | info.RegenInvisible = (tmp != 0) 141 | 142 | tmp, _ = strconv.Atoi(item.RegenBat) 143 | info.RegenBat = (tmp != 0) 144 | 145 | tmp, _ = strconv.Atoi(item.Master) 146 | info.Master = (tmp != 0) 147 | 148 | tmp, _ = strconv.Atoi(item.Chief) 149 | info.Chief = (tmp != 0) 150 | 151 | tmp, _ = strconv.Atoi(item.NormalRegin) 152 | info.NormalRegin = (tmp != 0) 153 | 154 | tmp, _ = strconv.Atoi(item.ClanType) 155 | info.ClanType = tmp 156 | 157 | tmp, _ = strconv.Atoi(item.MonsterClass) 158 | info.MonsterClass = tmp 159 | 160 | tmp, _ = strconv.Atoi(item.SkullType) 161 | info.SkullType = tmp 162 | } 163 | 164 | out, err := os.Create("monsterinfo__.json") 165 | if err != nil { 166 | panic(err) 167 | } 168 | defer out.Close() 169 | enc := json.NewEncoder(out) 170 | err = enc.Encode(infos) 171 | if err != nil { 172 | panic(err) 173 | } 174 | return 175 | } 176 | -------------------------------------------------------------------------------- /cmd/test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | // "fmt" 6 | "github.com/tiancaiamao/ouster/data" 7 | "github.com/tiancaiamao/ouster/packet" 8 | "os" 9 | ) 10 | 11 | const ( 12 | SKILL_RAPID_GLIDING uint16 = 203 13 | SKILL_METEOR_STRIKE uint16 = 180 14 | SKILL_INVISIBILITY uint16 = 100 15 | SKILL_PARALYZE uint16 = 89 16 | SKILL_BLOOD_SPEAR uint16 = 97 17 | 18 | SKILL_ABSORB_SOUL uint16 = 246 19 | SKILL_SUMMON_SYLPH uint16 = 247 20 | SKILL_SHARP_HAIL uint16 = 348 // 尖锐冰雹 21 | SKILL_FLOURISH uint16 = 219 // 活跃攻击 22 | SKILL_DESTRUCTION_SPEAR uint16 = 298 //致命爆发 23 | SKILL_SHARP_CHAKRAM uint16 = 295 // 税利之轮 24 | SKILL_EVADE uint16 = 220 // 回避术 25 | 26 | SKILL_FIRE_OF_SOUL_STONE uint16 = 227 27 | SKILL_ICE_OF_SOUL_STONE uint16 = 228 28 | SKILL_SAND_OF_SOUL_STONE uint16 = 229 29 | 30 | SKILL_TELEPORT uint16 = 280 // 瞬间移动 31 | SKILL_DUCKING_WALLOP uint16 = 302 // 光速冲击 32 | SKILL_DISTANCE_BLITZ uint16 = 304 // 雷神斩 33 | 34 | ) 35 | 36 | func main() { 37 | vampire := data.PCVampireInfo{ 38 | PCType: 'V', 39 | Name: "vampire", 40 | Level: 150, 41 | SkinColor: 420, 42 | Alignment: 7500, 43 | STR: [3]uint16{20, 20, 20}, 44 | DEX: [3]uint16{20, 20, 20}, 45 | INT: [3]uint16{20, 20, 20}, 46 | HP: [2]uint16{472, 472}, 47 | Rank: 50, 48 | RankExp: 10700, 49 | Exp: 125, 50 | Fame: 282, 51 | Sight: 13, 52 | Bonus: 9999, 53 | Competence: 1, 54 | GuildMemberRank: 4, 55 | AdvancementLevel: 100, 56 | ZoneID: 23, 57 | ZoneX: 145, 58 | ZoneY: 237, 59 | } 60 | 61 | skill := packet.VampireSkillInfo{ 62 | LearnNewSkill: false, 63 | SubVampireSkillInfoList: []packet.SubVampireSkillInfo{ 64 | packet.SubVampireSkillInfo{ 65 | SkillType: SKILL_RAPID_GLIDING, 66 | Interval: 50, 67 | CastingTime: 31, 68 | }, 69 | packet.SubVampireSkillInfo{ 70 | SkillType: SKILL_METEOR_STRIKE, 71 | Interval: 10, 72 | CastingTime: 4160749567, 73 | }, 74 | packet.SubVampireSkillInfo{ 75 | SkillType: SKILL_INVISIBILITY, 76 | Interval: 30, 77 | CastingTime: 11, 78 | }, 79 | packet.SubVampireSkillInfo{ 80 | SkillType: SKILL_PARALYZE, 81 | Interval: 60, 82 | CastingTime: 41, 83 | }, 84 | packet.SubVampireSkillInfo{ 85 | SkillType: SKILL_BLOOD_SPEAR, 86 | Interval: 60, 87 | CastingTime: 41, 88 | }, 89 | }, 90 | } 91 | 92 | // ouster := data.PCOusterInfo{ 93 | // PCType: 'O', 94 | // Name: "ouster", 95 | // Level: 150, 96 | // HairColor: 101, 97 | // Alignment: 7500, 98 | // STR: [3]uint16{10, 10, 10}, 99 | // DEX: [3]uint16{25, 25, 25}, 100 | // INT: [3]uint16{10, 10, 10}, 101 | // HP: [2]uint16{315, 315}, 102 | // MP: [2]uint16{186, 111}, 103 | // Rank: 50, 104 | // RankExp: 10700, 105 | // Exp: 125, 106 | // Fame: 500, 107 | // Gold: 68, 108 | // Sight: 13, 109 | // Bonus: 9999, 110 | // SkillBonus: 9999, 111 | // Competence: 1, 112 | // GuildID: 66, 113 | // GuildMemberRank: 4, 114 | // AdvancementLevel: 100, 115 | // 116 | // ZoneID: 62, 117 | // ZoneX: 62, 118 | // ZoneY: 58, 119 | // } 120 | // 121 | // skill := OusterSkillInfo{ 122 | // LearnNewSkill: false, 123 | // SubOusterSkillInfoList: [] SubOusterSkillInfo{ 124 | // SubOusterSkillInfo{ 125 | // SkillType: SKILL_FLOURISH, 126 | // ExpLevel: 1, 127 | // Interval: 10, 128 | // CastingTime: 6, 129 | // }, 130 | // SubOusterSkillInfo{ 131 | // SkillType: SKILL_ABSORB_SOUL, 132 | // ExpLevel: 1, 133 | // Interval: 5, 134 | // }, 135 | // SubOusterSkillInfo{ 136 | // SkillType: SKILL_SUMMON_SYLPH, 137 | // ExpLevel: 1, 138 | // Interval: 5, 139 | // }, 140 | // SubOusterSkillInfo{ 141 | // SkillType: SKILL_SHARP_HAIL, 142 | // ExpLevel: 1, 143 | // Interval: 112, 144 | // CastingTime: 107, 145 | // }, 146 | // SubOusterSkillInfo{ 147 | // SkillType: SKILL_DISTANCE_BLITZ, 148 | // ExpLevel: 1, 149 | // Interval: 70, 150 | // CastingTime: 65, 151 | // }, 152 | // SubOusterSkillInfo{ 153 | // SkillType: SKILL_DUCKING_WALLOP, 154 | // ExpLevel: 1, 155 | // Interval: 100, 156 | // CastingTime: 95, 157 | // }, 158 | // SubOusterSkillInfo{ 159 | // SkillType: SKILL_DESTRUCTION_SPEAR, 160 | // ExpLevel: 1, 161 | // Interval: 50, 162 | // CastingTime: 45, 163 | // }, 164 | // SubOusterSkillInfo{ 165 | // SkillType: SKILL_SHARP_CHAKRAM, 166 | // ExpLevel: 1, 167 | // Interval: 600, 168 | // CastingTime: 600, 169 | // }, 170 | // SubOusterSkillInfo{ 171 | // SkillType: SKILL_TELEPORT, 172 | // ExpLevel: 1, 173 | // Interval: 50, 174 | // CastingTime: 45, 175 | // }, 176 | // SubOusterSkillInfo{ 177 | // SkillType: SKILL_SUMMON_SYLPH, 178 | // ExpLevel: 1, 179 | // Interval: 5, 180 | // CastingTime: 0, 181 | // }, 182 | // SubOusterSkillInfo{ 183 | // SkillType: SKILL_ICE_OF_SOUL_STONE, 184 | // ExpLevel: 1, 185 | // Interval: 0, 186 | // }, 187 | // SubOusterSkillInfo{ 188 | // SkillType: SKILL_FIRE_OF_SOUL_STONE, 189 | // ExpLevel: 1, 190 | // Interval: 0, 191 | // }, 192 | // SubOusterSkillInfo{ 193 | // SkillType: SKILL_EVADE, 194 | // ExpLevel: 1, 195 | // Interval: 600, 196 | // CastingTime: 600, 197 | // }, 198 | // }, 199 | // } 200 | 201 | f, err := os.Create("/Users/genius/.ouster/player/vampire") 202 | if err != nil { 203 | panic(err) 204 | } 205 | 206 | encoder := json.NewEncoder(f) 207 | err = encoder.Encode(vampire) 208 | if err != nil { 209 | panic(err) 210 | } 211 | 212 | err = encoder.Encode(skill) 213 | if err != nil { 214 | panic(err) 215 | } 216 | return 217 | } 218 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | const ( 4 | DataDir = "/Users/genius/.ouster/" 5 | DataFilePath = "/Users/genius/project/src/github.com/tiancaiamao/ouster/data/" 6 | GameServerIP string = "192.168.1.102" 7 | GameServerPort string = ":9998" 8 | LoginServerPort string = ":9999" 9 | ) 10 | -------------------------------------------------------------------------------- /data/bloodbiblesigninfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "io" 7 | ) 8 | 9 | type BloodBibleSignInfo struct { 10 | OpenNum uint32 11 | SignList []ItemType_t 12 | } 13 | 14 | func (info *BloodBibleSignInfo) Size() uint32 { 15 | var sz uint32 16 | sz = 5 + uint32(len(info.SignList))*2 17 | return sz 18 | } 19 | 20 | func (info *BloodBibleSignInfo) Write(writer io.Writer) error { 21 | binary.Write(writer, binary.LittleEndian, info.OpenNum) 22 | num := uint8(len(info.SignList)) 23 | binary.Write(writer, binary.LittleEndian, num) 24 | for i := 0; i < len(info.SignList); i++ { 25 | binary.Write(writer, binary.LittleEndian, info.SignList[i]) 26 | } 27 | return nil 28 | } 29 | 30 | func (info *BloodBibleSignInfo) Read(reader io.Reader) { 31 | binary.Read(reader, binary.LittleEndian, &info.OpenNum) 32 | var num uint8 33 | binary.Read(reader, binary.LittleEndian, &num) 34 | info.SignList = make([]ItemType_t, num) 35 | for i := 0; i < int(num); i++ { 36 | binary.Read(reader, binary.LittleEndian, &info.SignList[i]) 37 | } 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /data/directivesetinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/tiancaiamao/ouster/config" 6 | "os" 7 | "path" 8 | ) 9 | 10 | type Directive struct { 11 | Conditions []int 12 | Action int 13 | Parameter int 14 | Ratio int 15 | Weight int 16 | } 17 | 18 | type DirectiveSetItem struct { 19 | ID int 20 | DirectiveSet DirectiveSet 21 | } 22 | 23 | type DirectiveSet struct { 24 | Directives []*Directive 25 | DeadDirectives []*Directive 26 | Name string 27 | 28 | bAttackAir bool 29 | bSeeSafeZone bool 30 | } 31 | 32 | var DirectiveSetTable map[int]DirectiveSet 33 | 34 | func init() { 35 | var array []DirectiveSetItem 36 | 37 | filePath := path.Join(config.DataFilePath, "directivesetinfo.json") 38 | file, err := os.Open(filePath) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | dec := json.NewDecoder(file) 44 | err = dec.Decode(&array) 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | DirectiveSetTable = make(map[int]DirectiveSet) 50 | for _, v := range array { 51 | DirectiveSetTable[v.ID] = v.DirectiveSet 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /data/effectinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | type EffectInfo struct { 9 | EList []uint16 10 | } 11 | 12 | func (info *EffectInfo) Read(reader io.Reader) error { 13 | var num uint8 14 | binary.Read(reader, binary.LittleEndian, &num) 15 | info.EList = make([]uint16, num) 16 | for i := 0; i < int(num); i++ { 17 | binary.Read(reader, binary.LittleEndian, &info.EList[i]) 18 | } 19 | return nil 20 | } 21 | 22 | func (info *EffectInfo) Write(writer io.Writer) error { 23 | num := len(info.EList) 24 | binary.Write(writer, binary.LittleEndian, uint8(num)) 25 | for i := 0; i < int(num); i++ { 26 | binary.Write(writer, binary.LittleEndian, &info.EList[i]) 27 | } 28 | return nil 29 | } 30 | 31 | func (info *EffectInfo) Size() uint32 { 32 | return uint32(1 + len(info.EList)*2) 33 | } 34 | -------------------------------------------------------------------------------- /data/extrainfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | type ExtraInfo struct { 9 | ExtraSlotInfoList []PCItemInfo 10 | } 11 | 12 | func (info *ExtraInfo) Size() uint32 { 13 | var sz uint32 14 | sz = 1 15 | for i := 0; i < len(info.ExtraSlotInfoList); i++ { 16 | sz += info.ExtraSlotInfoList[i].Size() 17 | } 18 | return sz 19 | } 20 | 21 | func (info *ExtraInfo) Read(reader io.Reader) error { 22 | var num uint8 23 | binary.Read(reader, binary.LittleEndian, &num) 24 | info.ExtraSlotInfoList = make([]PCItemInfo, num) 25 | for i := 0; i < int(num); i++ { 26 | info.ExtraSlotInfoList[i].Read(reader) 27 | } 28 | return nil 29 | } 30 | 31 | func (info *ExtraInfo) Write(writer io.Writer) error { 32 | num := uint8(len(info.ExtraSlotInfoList)) 33 | binary.Write(writer, binary.LittleEndian, num) 34 | for i := 0; i < int(num); i++ { 35 | info.ExtraSlotInfoList[i].Write(writer) 36 | } 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /data/gearinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | // . "github.com/tiancaiamao/ouster/util" 7 | ) 8 | 9 | type GearInfo struct { 10 | GearSlotInfoList []GearSlotInfo 11 | } 12 | 13 | func (info *GearInfo) Size() uint32 { 14 | sz := uint32(1) 15 | for i := 0; i < len(info.GearSlotInfoList); i++ { 16 | sz += info.GearSlotInfoList[i].Size() 17 | } 18 | return sz 19 | } 20 | 21 | func (info *GearInfo) Read(reader io.Reader) error { 22 | var num uint8 23 | err := binary.Read(reader, binary.LittleEndian, &num) 24 | if err != nil { 25 | return err 26 | } 27 | info.GearSlotInfoList = make([]GearSlotInfo, num) 28 | for i := 0; i < int(num); i++ { 29 | info.GearSlotInfoList[i].Read(reader) 30 | } 31 | return nil 32 | } 33 | 34 | func (info *GearInfo) Write(writer io.Writer) error { 35 | num := uint8(len(info.GearSlotInfoList)) 36 | binary.Write(writer, binary.LittleEndian, num) 37 | for i := 0; i < int(num); i++ { 38 | info.GearSlotInfoList[i].Write(writer) 39 | } 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /data/gearinfo_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "bytes" 5 | "github.com/tiancaiamao/ouster/util" 6 | "testing" 7 | ) 8 | 9 | func TestGearInfoWrite(t *testing.T) { 10 | info := &GearInfo{ 11 | GearSlotInfoList: []GearSlotInfo{ 12 | GearSlotInfo{PCItemInfo: PCItemInfo{ObjectID: 0x76de, IClass: 0x23, ItemType: 0x0, OptionType: []util.OptionType_t{}, Durability: 0xbb8, Silver: 0x0, Grade: 4, EnchantLevel: 0x0, ItemNum: 0x1, MainColor: 0x0, SubItemInfoList: []SubItemInfo{}}, SlotID: 0x0}, 13 | GearSlotInfo{PCItemInfo: PCItemInfo{ObjectID: 0x76df00, IClass: 0x0, ItemType: 0x16, OptionType: []util.OptionType_t{}, Durability: 0xdac00, Silver: 0x0, Grade: 1024, EnchantLevel: 0x0, ItemNum: 0x0, MainColor: 0x0, SubItemInfoList: []SubItemInfo{}}, SlotID: 0x0}, 14 | GearSlotInfo{PCItemInfo: PCItemInfo{ObjectID: 0x76e00002, IClass: 0x0, ItemType: 0x400, OptionType: []util.OptionType_t{0x0, 0x0}, Durability: 0x1, Silver: 0x0, Grade: -1, EnchantLevel: 0x0, ItemNum: 0x14, MainColor: 0x0, SubItemInfoList: []SubItemInfo{}}, SlotID: 0x4}, 15 | GearSlotInfo{PCItemInfo: PCItemInfo{ObjectID: 0x76e100, IClass: 0x0, ItemType: 0xf, OptionType: []util.OptionType_t{}, Durability: 0x157c00, Silver: 0x0, Grade: 1024, EnchantLevel: 0x0, ItemNum: 0x0, MainColor: 0x1, SubItemInfoList: []SubItemInfo{}}, SlotID: 0x0}, 16 | GearSlotInfo{PCItemInfo: PCItemInfo{ObjectID: 0x76e20302, IClass: 0x0, ItemType: 0xe00, OptionType: []util.OptionType_t{}, Durability: 0x11940000, Silver: 0x0, Grade: 262144, EnchantLevel: 0x0, ItemNum: 0x0, MainColor: 0x100, SubItemInfoList: []SubItemInfo{}}, SlotID: 0x0}, 17 | GearSlotInfo{PCItemInfo: PCItemInfo{ObjectID: 0xe3030400, IClass: 0x76, ItemType: 0x0, OptionType: []util.OptionType_t{0x0}, Durability: 0x10000, Silver: 0x0, Grade: -65536, EnchantLevel: 0xff, ItemNum: 0xff, MainColor: 0x900, SubItemInfoList: []SubItemInfo{}}, SlotID: 0x0}, 18 | GearSlotInfo{PCItemInfo: PCItemInfo{ObjectID: 0xe4040900, IClass: 0x76, ItemType: 0x0, OptionType: []util.OptionType_t{0x5}, Durability: 0x10000, Silver: 0x0, Grade: -65536, EnchantLevel: 0xff, ItemNum: 0xff, MainColor: 0x900, SubItemInfoList: []SubItemInfo{}}, SlotID: 0x0}, 19 | }, 20 | } 21 | buf := &bytes.Buffer{} 22 | info.Write(buf) 23 | right := []byte{7, 222, 118, 0, 0, 35, 0, 0, 0, 184, 11, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 223, 118, 0, 0, 22, 0, 0, 0, 172, 13, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 224, 118, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 20, 0, 0, 0, 4, 0, 225, 118, 0, 0, 15, 0, 0, 0, 124, 21, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 226, 118, 0, 0, 14, 0, 0, 0, 148, 17, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 4, 3, 227, 118, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0, 0, 9, 4, 228, 118, 0, 0, 1, 5, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0} 24 | if !bytes.Equal(right, buf.Bytes()) { 25 | t.Errorf("GearInfo error") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data/gearslotinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "io" 7 | ) 8 | 9 | type GearSlotInfo struct { 10 | PCItemInfo 11 | 12 | SlotID SlotID_t 13 | } 14 | 15 | func (info *GearSlotInfo) Read(reader io.Reader) error { 16 | info.PCItemInfo.Read(reader) 17 | err := binary.Read(reader, binary.LittleEndian, &info.SlotID) 18 | return err 19 | } 20 | 21 | func (info *GearSlotInfo) Write(writer io.Writer) error { 22 | err := info.PCItemInfo.Write(writer) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | err = binary.Write(writer, binary.LittleEndian, info.SlotID) 28 | return err 29 | } 30 | func (info *GearSlotInfo) Size() uint32 { 31 | return 1 + info.PCItemInfo.Size() 32 | } 33 | -------------------------------------------------------------------------------- /data/inventoryinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | type InventoryInfo struct { 9 | InventorySlotInfoList []InventorySlotInfo 10 | } 11 | 12 | func (info *InventoryInfo) Size() uint32 { 13 | sz := uint32(1) 14 | for i := 0; i < len(info.InventorySlotInfoList); i++ { 15 | sz += info.InventorySlotInfoList[i].Size() 16 | } 17 | return sz 18 | } 19 | 20 | func (info *InventoryInfo) Write(writer io.Writer) error { 21 | num := uint8(len(info.InventorySlotInfoList)) 22 | binary.Write(writer, binary.LittleEndian, num) 23 | for i := 0; i < int(num); i++ { 24 | info.InventorySlotInfoList[i].Write(writer) 25 | } 26 | return nil 27 | } 28 | 29 | func (info *InventoryInfo) Read(reader io.Reader) error { 30 | var num uint8 31 | binary.Read(reader, binary.LittleEndian, &num) 32 | info.InventorySlotInfoList = make([]InventorySlotInfo, num) 33 | for i := 0; i < int(num); i++ { 34 | info.InventorySlotInfoList[i].Read(reader) 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /data/inventoryslotinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "io" 7 | ) 8 | 9 | type InventorySlotInfo struct { 10 | PCItemInfo 11 | 12 | InvenX CoordInven_t 13 | InvenY CoordInven_t 14 | } 15 | 16 | func (slot *InventorySlotInfo) Read(reader io.Reader) error { 17 | slot.PCItemInfo.Read(reader) 18 | 19 | binary.Read(reader, binary.LittleEndian, &slot.InvenX) 20 | binary.Read(reader, binary.LittleEndian, &slot.InvenY) 21 | return nil 22 | } 23 | 24 | func (slot *InventorySlotInfo) Write(writer io.Writer) error { 25 | slot.PCItemInfo.Write(writer) 26 | 27 | binary.Write(writer, binary.LittleEndian, slot.InvenX) 28 | binary.Write(writer, binary.LittleEndian, slot.InvenY) 29 | return nil 30 | } 31 | 32 | func (info *InventorySlotInfo) Size() uint32 { 33 | return 2 + info.PCItemInfo.Size() 34 | } 35 | -------------------------------------------------------------------------------- /data/map.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | // "log" 8 | ) 9 | 10 | const ( 11 | TILE_GROUND_BLOCKED = iota 12 | TILE_AIR_BLOCKED 13 | TILE_UNDERGROUND_BLOCKED 14 | TILE_WALKING_CREATURE 15 | TILE_FLYING_CREATURE 16 | TILE_BURROWING_CREATURE 17 | TILE_ITEM 18 | TILE_OBSTACLE 19 | TILE_EFFECT 20 | TILE_BUILDING 21 | TILE_PORTAL 22 | TILE_TERRAIN 23 | ) 24 | 25 | type Map struct { 26 | Name string 27 | Desc string 28 | ZoneType uint8 29 | ZoneID uint16 30 | ZoneLevel uint8 31 | Width uint16 32 | Height uint16 33 | Data []byte 34 | MonsterInfo []Monster 35 | } 36 | 37 | func Load(smp io.Reader) (*Map, error) { 38 | var tmp [32]byte 39 | 40 | _, err := smp.Read(tmp[:4]) 41 | if err != nil { 42 | return nil, errors.New("read version length error") 43 | } 44 | versionLen := binary.LittleEndian.Uint32(tmp[:4]) 45 | // log.Println("versionLen=", versionLen) 46 | 47 | version := make([]byte, versionLen) 48 | _, err = smp.Read(version) 49 | if err != nil { 50 | return nil, errors.New("read version error") 51 | } 52 | // log.Println("version=", string(version)) 53 | 54 | _, err = smp.Read(tmp[:2]) 55 | if err != nil { 56 | return nil, errors.New("read zone id error") 57 | } 58 | zoneID := binary.LittleEndian.Uint16(tmp[:2]) 59 | // log.Println("zoneID=", zoneID) 60 | 61 | _, err = smp.Read(tmp[:2]) 62 | if err != nil { 63 | return nil, errors.New("read zone group id error") 64 | } 65 | // zoneGroupID := binary.LittleEndian.Uint16(tmp[:2]) 66 | // log.Println("zoneGroupID=", zoneGroupID) 67 | 68 | _, err = smp.Read(tmp[:4]) 69 | if err != nil { 70 | return nil, errors.New("read zone name length error") 71 | } 72 | // log.Println("zonenameLen byte = ", tmp[:4]) 73 | zonenameLen := binary.LittleEndian.Uint32(tmp[:4]) 74 | // log.Println("zonenameLen=", zonenameLen) 75 | 76 | var zoneName []byte 77 | if zonenameLen > 0 { 78 | zoneName := make([]byte, zonenameLen) 79 | _, err = smp.Read(zoneName) 80 | if err != nil { 81 | return nil, errors.New("read zone name error") 82 | } 83 | 84 | // log.Println("zoneName=", zoneName) 85 | } 86 | 87 | _, err = smp.Read(tmp[:1]) 88 | if err != nil { 89 | return nil, errors.New("read zone type error") 90 | } 91 | zoneType := tmp[0] 92 | // log.Println("zoneType=", zoneType) 93 | 94 | _, err = smp.Read(tmp[:1]) 95 | if err != nil { 96 | return nil, errors.New("read zone level error") 97 | } 98 | zoneLevel := tmp[0] 99 | // log.Println("zoneLevel=", zoneLevel) 100 | 101 | _, err = smp.Read(tmp[:4]) 102 | descLen := binary.LittleEndian.Uint32(tmp[:4]) 103 | // log.Println("descLen=", descLen) 104 | 105 | var desc []byte 106 | if descLen > 0 { 107 | desc := make([]byte, descLen) 108 | _, err = smp.Read(desc) 109 | if err != nil { 110 | return nil, errors.New("read zone description error") 111 | } 112 | // log.Println("desc=", desc) 113 | } 114 | 115 | _, err = smp.Read(tmp[:2]) 116 | if err != nil { 117 | return nil, errors.New("read width error") 118 | } 119 | width := binary.LittleEndian.Uint16(tmp[:2]) 120 | // log.Println("width=", width) 121 | 122 | _, err = smp.Read(tmp[:2]) 123 | if err != nil { 124 | return nil, errors.New("read height error") 125 | } 126 | height := binary.LittleEndian.Uint16(tmp[:2]) 127 | // log.Println("height=", height) 128 | 129 | // NOTE: use width*height directly overflow uint16 and get 0 !!! 130 | flags := make([]byte, int(width)*int(height)) 131 | _, err = smp.Read(flags) 132 | if err != nil { 133 | return nil, errors.New("read flag error") 134 | } 135 | 136 | // for y := 0; y < height; y++ { 137 | // for x := 0; x < width; x++ { 138 | // 139 | // } 140 | // } 141 | return &Map{ 142 | Name: string(zoneName), 143 | Desc: string(desc), 144 | ZoneType: zoneType, 145 | ZoneID: zoneID, 146 | ZoneLevel: zoneLevel, 147 | Width: width, 148 | Height: height, 149 | Data: flags, 150 | }, nil 151 | } 152 | -------------------------------------------------------------------------------- /data/monster.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiancaiamao/ouster/ebd50bff6ead1bafea8b29e5cad42aaafb747c0e/data/monster.json -------------------------------------------------------------------------------- /data/monsterinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/tiancaiamao/ouster/config" 6 | . "github.com/tiancaiamao/ouster/util" 7 | "os" 8 | "path" 9 | ) 10 | 11 | type MonsterInfo struct { 12 | MonsterType MonsterType_t 13 | Name string 14 | Level Level_t 15 | STR Attr_t 16 | DEX Attr_t 17 | INTE Attr_t 18 | BodySize int 19 | Exp Exp_t 20 | MColor Color_t 21 | SColor Color_t 22 | Sight Sight_t 23 | MoveMode MoveMode 24 | MeleeRange int 25 | MissileRange int 26 | AIType int 27 | UnburrowChance int 28 | SType int 29 | Fame Fame_t 30 | Align int 31 | AOrder int 32 | Moral int 33 | Delay int 34 | RegenPortal bool 35 | RegenInvisible bool 36 | RegenBat bool 37 | Master bool 38 | ClanType int 39 | Chief bool 40 | NormalRegin bool 41 | MonsterClass int 42 | SkullType int 43 | } 44 | 45 | var MonsterInfoTable map[MonsterType_t]MonsterInfo 46 | 47 | func init() { 48 | var array []MonsterInfo 49 | 50 | filePath := path.Join(config.DataFilePath, "monsterinfo.json") 51 | file, err := os.Open(filePath) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | dec := json.NewDecoder(file) 57 | err = dec.Decode(&array) 58 | if err != nil { 59 | panic(err) 60 | } 61 | 62 | MonsterInfoTable = make(map[MonsterType_t]MonsterInfo) 63 | for _, v := range array { 64 | MonsterInfoTable[v.MonsterType] = v 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /data/nicknameinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | type NicknameInfo struct { 10 | NicknameID uint16 11 | NicknameType uint8 12 | Nickname string 13 | NicknameIndex uint16 14 | } 15 | 16 | func (info *NicknameInfo) Size() uint32 { 17 | return 3 18 | } 19 | 20 | func (info *NicknameInfo) Write(writer io.Writer) error { 21 | binary.Write(writer, binary.LittleEndian, info.NicknameID) 22 | binary.Write(writer, binary.LittleEndian, uint8(0)) 23 | return nil 24 | } 25 | 26 | func (info *NicknameInfo) Read(reader io.Reader) error { 27 | binary.Read(reader, binary.LittleEndian, &info.NicknameID) 28 | binary.Read(reader, binary.LittleEndian, &info.NicknameType) 29 | 30 | switch info.NicknameType { 31 | case 0: 32 | break 33 | default: 34 | return errors.New("not implement!") 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /data/npcinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "io" 7 | ) 8 | 9 | type NPCInfo struct { 10 | Name string 11 | NPCID NPCID_t 12 | X ZoneCoord_t 13 | Y ZoneCoord_t 14 | } 15 | 16 | func (info *NPCInfo) Size() uint32 { 17 | if len(info.Name) > 0 { 18 | return 7 + uint32(len(info.Name)) 19 | } 20 | return 1 21 | } 22 | 23 | func (info *NPCInfo) Read(reader io.Reader) error { 24 | var szName uint8 25 | var buf [256]byte 26 | binary.Read(reader, binary.LittleEndian, &szName) 27 | if szName > 0 { 28 | _, err := reader.Read(buf[:szName]) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | info.Name = string(buf[:szName]) 34 | binary.Read(reader, binary.LittleEndian, &info.NPCID) 35 | binary.Read(reader, binary.LittleEndian, &info.X) 36 | binary.Read(reader, binary.LittleEndian, &info.Y) 37 | } 38 | return nil 39 | } 40 | 41 | func (info *NPCInfo) Write(writer io.Writer) error { 42 | binary.Write(writer, binary.LittleEndian, uint8(len(info.Name))) 43 | if len(info.Name) > 0 { 44 | io.WriteString(writer, info.Name) 45 | binary.Write(writer, binary.LittleEndian, info.NPCID) 46 | binary.Write(writer, binary.LittleEndian, info.X) 47 | binary.Write(writer, binary.LittleEndian, info.Y) 48 | } 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /data/pcinfo_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "bytes" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "testing" 7 | ) 8 | 9 | func TestPCOusterInfoWrite(t *testing.T) { 10 | info := PCOusterInfo{ 11 | ObjectID: 1, 12 | Name: "test", 13 | Level: 150, 14 | 15 | HairColor: 101, 16 | 17 | Alignment: 7500, 18 | STR: [3]Attr_t{10, 10, 10}, 19 | DEX: [3]Attr_t{25, 25, 25}, 20 | INI: [3]Attr_t{10, 10, 10}, 21 | 22 | HP: [2]HP_t{315, 315}, 23 | MP: [2]MP_t{186, 111}, 24 | 25 | Rank: 50, 26 | RankExp: 10700, 27 | Exp: 125, 28 | 29 | Fame: 500, 30 | Gold: 68, 31 | Sight: 13, 32 | Bonus: 9999, 33 | SkillBonus: 9999, 34 | 35 | Competence: 1, 36 | GuildID: 66, 37 | 38 | GuildMemberRank: 4, 39 | AdvancementLevel: 100, 40 | } 41 | 42 | buf := &bytes.Buffer{} 43 | info.Write(buf) 44 | 45 | right := []byte{1, 0, 0, 0, 4, 116, 101, 115, 116, 150, 0, 101, 0, 0, 76, 29, 0, 0, 10, 0, 10, 0, 10, 0, 25, 0, 25, 0, 25, 0, 10, 0, 10, 0, 10, 0, 59, 1, 59, 1, 186, 0, 111, 0, 50, 204, 41, 0, 0, 125, 0, 0, 0, 244, 1, 0, 0, 68, 0, 0, 0, 13, 15, 39, 15, 39, 0, 0, 1, 66, 0, 0, 4, 0, 0, 0, 0, 100, 0, 0, 0, 0} 46 | if !bytes.Equal(buf.Bytes(), right) { 47 | t.Failed() 48 | } 49 | 50 | slayerInfo := &PCSlayerInfo{ 51 | ObjectID: 0x76dd, 52 | Name: "test", 53 | Sex: 1, 54 | HairStyle: 1, 55 | HairColor: 0x17d, 56 | SkinColor: 0x1e0, 57 | MasterEffectColor: 0x0, 58 | Alignment: 7500, 59 | STR: [3]Attr_t{0xd, 0xd, 0xd}, 60 | DEX: [3]Attr_t{0xe, 0xe, 0xe}, 61 | INI: [3]Attr_t{0x14, 0x14, 0x14}, 62 | STRExp: 0x4f8, 63 | DEXExp: 0x5dd, 64 | INIExp: 0xfd3, 65 | Rank: 0x1, 66 | RankExp: 0x29cc, 67 | HP: [2]HP_t{0x36, 0x36}, 68 | MP: [2]MP_t{0x32, 0x32}, 69 | Fame: 0x0, 70 | Gold: 0x1f4, 71 | DomainLevels: [6]SkillLevel_t{0x0, 0x0, 0x0, 0x7, 0x0, 0x0}, 72 | DomainExps: [6]SkillExp_t{0x32, 0x32, 0x28, 0x366, 0x1e, 0xf4240}, 73 | Sight: 0xd, 74 | HotKey: [4]SkillType_t{0x4972, 0x39, 0x0, 0xd748}, 75 | Competence: 0x1, 76 | GuildID: 0x63, GuildName: "", 77 | GuildMemberRank: 0x0, 78 | UnionID: 0x4, 79 | AdvancementLevel: 0x0, 80 | AdvancementGoalExp: 0x0, 81 | AttrBonus: 0x0, 82 | ZoneID: 0x0, 83 | ZoneX: 0x0, 84 | ZoneY: 0x0, 85 | } 86 | buf.Reset() 87 | slayerInfo.Write(buf) 88 | right = []byte{221, 118, 0, 0, 4, 116, 101, 115, 116, 1, 1, 125, 1, 224, 1, 0, 76, 29, 0, 0, 13, 0, 13, 0, 13, 0, 14, 0, 14, 0, 14, 0, 20, 0, 20, 0, 20, 0, 1, 204, 41, 0, 0, 248, 4, 0, 0, 221, 5, 0, 0, 211, 15, 0, 0, 54, 0, 54, 0, 50, 0, 50, 0, 0, 0, 0, 0, 244, 1, 0, 0, 0, 50, 0, 0, 0, 0, 50, 0, 0, 0, 0, 40, 0, 0, 0, 7, 102, 3, 0, 0, 0, 30, 0, 0, 0, 0, 64, 66, 15, 0, 13, 114, 73, 57, 0, 0, 0, 72, 215, 1, 99, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 89 | if !bytes.Equal(buf.Bytes(), right) { 90 | t.Failed() 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /data/pciteminfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "io" 7 | ) 8 | 9 | type PCItemInfo struct { 10 | ObjectID ObjectID_t 11 | IClass byte 12 | ItemType ItemType_t 13 | OptionType []OptionType_t 14 | Durability Durability_t 15 | Silver Silver_t 16 | Grade Grade_t 17 | EnchantLevel EnchantLevel_t 18 | ItemNum ItemNum_t 19 | MainColor uint16 20 | SubItemInfoList []SubItemInfo 21 | } 22 | 23 | func (info *PCItemInfo) Size() uint32 { 24 | var sz uint32 25 | sz = 4 + 1 + 2 + 1 + 4 + 2 + 4 + 1 + 1 + 2 + 1 26 | for i := 0; i < len(info.SubItemInfoList); i++ { 27 | sz += info.SubItemInfoList[i].Size() 28 | } 29 | return sz 30 | } 31 | 32 | func (info *PCItemInfo) Read(reader io.Reader) { 33 | binary.Read(reader, binary.LittleEndian, &info.ObjectID) 34 | binary.Read(reader, binary.LittleEndian, &info.IClass) 35 | binary.Read(reader, binary.LittleEndian, &info.ItemType) 36 | 37 | var optionSize uint8 38 | binary.Read(reader, binary.LittleEndian, &optionSize) 39 | info.OptionType = make([]OptionType_t, optionSize) 40 | for i := 0; i < int(optionSize); i++ { 41 | binary.Read(reader, binary.LittleEndian, &info.OptionType[i]) 42 | } 43 | 44 | binary.Read(reader, binary.LittleEndian, &info.Durability) 45 | binary.Read(reader, binary.LittleEndian, &info.Silver) 46 | binary.Read(reader, binary.LittleEndian, &info.Grade) 47 | binary.Read(reader, binary.LittleEndian, &info.EnchantLevel) 48 | binary.Read(reader, binary.LittleEndian, &info.ItemNum) 49 | binary.Read(reader, binary.LittleEndian, &info.MainColor) 50 | 51 | var num uint8 52 | binary.Read(reader, binary.LittleEndian, &num) 53 | info.SubItemInfoList = make([]SubItemInfo, num) 54 | for i := 0; i < int(num); i++ { 55 | info.SubItemInfoList[i].Read(reader) 56 | } 57 | } 58 | 59 | func (info *PCItemInfo) Write(writer io.Writer) error { 60 | binary.Write(writer, binary.LittleEndian, info.ObjectID) 61 | binary.Write(writer, binary.LittleEndian, info.IClass) 62 | binary.Write(writer, binary.LittleEndian, info.ItemType) 63 | 64 | optionSize := uint8(len(info.OptionType)) 65 | binary.Write(writer, binary.LittleEndian, optionSize) 66 | for i := 0; i < int(optionSize); i++ { 67 | binary.Write(writer, binary.LittleEndian, &info.OptionType[i]) 68 | } 69 | 70 | binary.Write(writer, binary.LittleEndian, info.Durability) 71 | binary.Write(writer, binary.LittleEndian, info.Silver) 72 | binary.Write(writer, binary.LittleEndian, info.Grade) 73 | binary.Write(writer, binary.LittleEndian, info.EnchantLevel) 74 | binary.Write(writer, binary.LittleEndian, info.ItemNum) 75 | binary.Write(writer, binary.LittleEndian, info.MainColor) 76 | 77 | var num uint8 78 | binary.Write(writer, binary.LittleEndian, num) 79 | for i := 0; i < int(num); i++ { 80 | info.SubItemInfoList[i].Write(writer) 81 | } 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /data/pcskillinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "io" 7 | ) 8 | 9 | type PCSkillInfo interface { 10 | Write(io.Writer) error 11 | Size() uint32 12 | } 13 | type VampireSkillInfo struct { 14 | LearnNewSkill bool 15 | SubVampireSkillInfoList []SubVampireSkillInfo 16 | } 17 | 18 | func (info VampireSkillInfo) Size() uint32 { 19 | var sz uint32 20 | sz = 2 21 | for i := 0; i < len(info.SubVampireSkillInfoList); i++ { 22 | sz += info.SubVampireSkillInfoList[i].Size() 23 | } 24 | return sz 25 | } 26 | func (info VampireSkillInfo) Write(writer io.Writer) error { 27 | if info.LearnNewSkill { 28 | binary.Write(writer, binary.LittleEndian, uint8(1)) 29 | } else { 30 | binary.Write(writer, binary.LittleEndian, uint8(0)) 31 | } 32 | 33 | binary.Write(writer, binary.LittleEndian, uint8(len(info.SubVampireSkillInfoList))) 34 | for _, v := range info.SubVampireSkillInfoList { 35 | v.Write(writer) 36 | } 37 | return nil 38 | } 39 | 40 | type SubVampireSkillInfo struct { 41 | SkillType SkillType_t 42 | Interval uint32 43 | CastingTime uint32 44 | } 45 | 46 | func (info *SubVampireSkillInfo) Size() uint32 { 47 | return 10 48 | } 49 | func (info SubVampireSkillInfo) Write(writer io.Writer) error { 50 | binary.Write(writer, binary.LittleEndian, info.SkillType) 51 | binary.Write(writer, binary.LittleEndian, info.Interval) 52 | binary.Write(writer, binary.LittleEndian, info.CastingTime) 53 | return nil 54 | } 55 | 56 | type OusterSkillInfo struct { 57 | LearnNewSkill bool 58 | SubOusterSkillInfoList []SubOusterSkillInfo 59 | } 60 | 61 | func (info OusterSkillInfo) Size() uint32 { 62 | var sz uint32 63 | sz = 2 64 | for i := 0; i < len(info.SubOusterSkillInfoList); i++ { 65 | sz += info.SubOusterSkillInfoList[i].Size() 66 | } 67 | return sz 68 | } 69 | 70 | func (info OusterSkillInfo) Write(writer io.Writer) error { 71 | if info.LearnNewSkill { 72 | binary.Write(writer, binary.LittleEndian, uint8(1)) 73 | } else { 74 | binary.Write(writer, binary.LittleEndian, uint8(0)) 75 | } 76 | 77 | binary.Write(writer, binary.LittleEndian, uint8(len(info.SubOusterSkillInfoList))) 78 | for _, v := range info.SubOusterSkillInfoList { 79 | v.Write(writer) 80 | } 81 | return nil 82 | } 83 | 84 | type SubOusterSkillInfo struct { 85 | SkillType SkillType_t 86 | ExpLevel ExpLevel_t 87 | Interval uint32 88 | CastingTime uint32 89 | } 90 | 91 | func (info *SubOusterSkillInfo) Size() uint32 { 92 | return 12 93 | } 94 | 95 | func (info SubOusterSkillInfo) Write(writer io.Writer) error { 96 | binary.Write(writer, binary.LittleEndian, info.SkillType) 97 | binary.Write(writer, binary.LittleEndian, info.ExpLevel) 98 | binary.Write(writer, binary.LittleEndian, info.Interval) 99 | binary.Write(writer, binary.LittleEndian, info.CastingTime) 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /data/size_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | type Dumper interface { 11 | Write(io.Writer) error 12 | Size() uint32 13 | } 14 | 15 | func TestSize(t *testing.T) { 16 | arr := []Dumper{ 17 | &GearInfo{}, 18 | &BloodBibleSignInfo{}, 19 | &EffectInfo{}, 20 | &ExtraInfo{}, 21 | &GearSlotInfo{}, 22 | &InventoryInfo{}, 23 | &InventorySlotInfo{}, 24 | &NPCInfo{}, 25 | // &PCInfo{}, 26 | &PCItemInfo{}, 27 | VampireSkillInfo{}, 28 | OusterSkillInfo{}, 29 | // SlayerSkillInfo{}, 30 | &SubItemInfo{}, 31 | &NicknameInfo{}, 32 | } 33 | 34 | buf := &bytes.Buffer{} 35 | for _, v := range arr { 36 | buf.Reset() 37 | v.Write(buf) 38 | if uint32(buf.Len()) != v.Size() { 39 | t.Errorf("%s的Size不对:期待%d 实际%d\n", reflect.TypeOf(v), buf.Len(), v.Size()) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /data/smp.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | . "github.com/tiancaiamao/ouster/util" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | ) 11 | 12 | type SMP struct { 13 | Version string 14 | ZoneGroupID uint8 15 | Name string 16 | Description string 17 | ZoneID ZoneID_t 18 | ZoneType ZoneType 19 | ZoneLevel ZoneLevel_t 20 | 21 | ZoneAccessMode ZoneAccessMode 22 | DarkLevel DarkLevel_t 23 | LightLevel LightLevel_t 24 | Width ZoneCoord_t 25 | Height ZoneCoord_t 26 | 27 | Data *bytes.Buffer 28 | } 29 | 30 | func ReadSMP(fileName string) (*SMP, error) { 31 | fd, err := os.Open(fileName) 32 | if err != nil { 33 | return nil, err 34 | } 35 | defer fd.Close() 36 | 37 | ret := new(SMP) 38 | ret.Data = &bytes.Buffer{} 39 | 40 | // read zone version 41 | var buf [200]byte 42 | var strLen uint32 43 | binary.Read(fd, binary.LittleEndian, &strLen) 44 | skip := io.LimitReader(fd, int64(strLen)) 45 | ioutil.ReadAll(skip) 46 | 47 | // read zone id 48 | binary.Read(fd, binary.LittleEndian, &ret.ZoneID) 49 | 50 | // read zone group id 51 | var ignore uint16 52 | binary.Read(fd, binary.LittleEndian, &ignore) 53 | 54 | // read zone name 55 | binary.Read(fd, binary.LittleEndian, &strLen) 56 | if strLen > 0 { 57 | fd.Read(buf[:strLen]) 58 | } 59 | 60 | // read zone type & level 61 | binary.Read(fd, binary.LittleEndian, &ret.ZoneType) 62 | binary.Read(fd, binary.LittleEndian, &ret.ZoneLevel) 63 | 64 | // read zone description 65 | binary.Read(fd, binary.LittleEndian, &strLen) 66 | if strLen > 0 { 67 | fd.Read(buf[:strLen]) 68 | } 69 | 70 | // read zone width & height 71 | binary.Read(fd, binary.LittleEndian, &ret.Width) 72 | binary.Read(fd, binary.LittleEndian, &ret.Height) 73 | 74 | io.Copy(ret.Data, fd) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | return ret, nil 80 | } 81 | 82 | type SSIRecord struct { 83 | Level ZoneLevel_t 84 | Left uint8 85 | Top uint8 86 | Right uint8 87 | Bottom uint8 88 | } 89 | 90 | type SSI []SSIRecord 91 | 92 | func ReadSSI(fileName string) (SSI, error) { 93 | fd, err := os.Open(fileName) 94 | if err != nil { 95 | return nil, err 96 | } 97 | defer fd.Close() 98 | 99 | var size uint32 100 | err = binary.Read(fd, binary.LittleEndian, &size) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | ret := make(SSI, size) 106 | var buf [5]byte 107 | for i := 0; i < int(size); i++ { 108 | _, err = fd.Read(buf[:]) 109 | if err != nil { 110 | return nil, err 111 | } 112 | ret[i].Level = ZoneLevel_t(buf[0]) 113 | ret[i].Left = buf[1] 114 | ret[i].Top = buf[2] 115 | ret[i].Right = buf[3] 116 | ret[i].Bottom = buf[4] 117 | } 118 | return ret, nil 119 | } 120 | -------------------------------------------------------------------------------- /data/smp_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestReadSMP(t *testing.T) { 8 | files := []string{ 9 | "adam_c.smp", 10 | "adam_e.smp", 11 | "adam_new_c.smp", 12 | "adam_new_c_n.smp", 13 | "adam_new_c_s.smp", 14 | "adam_new_e.smp", 15 | "adam_new_w.smp", 16 | "adam_w.smp", 17 | "alter_of_blood.smp", 18 | "asylion_dungeon.smp", 19 | "bathory_battlezone.smp", 20 | "bathory_dungeon_b1f.smp", 21 | "bathory_dungeon_b2f.smp", 22 | "bathory_dungeon_b3f.smp", 23 | "bathory_dungeon_b4f.smp", 24 | "bathory_lair.smp", 25 | "bathory_lair_clon.smp", 26 | "caligo_dungeon.smp", 27 | "castalo_ne.smp", 28 | "castellum.smp", 29 | "castle_hexserius.smp", 30 | "castle_octavus.smp", 31 | "castle_pentanus.smp", 32 | "castle_quartus.smp", 33 | "castle_rasen_1_1.smp", 34 | "castle_rasen_1_2.smp", 35 | "castle_rasen_2_2.smp", 36 | "castle_septimus.smp", 37 | "castle_tertius.smp", 38 | "clan_hdqrs.smp", 39 | "devt.smp", 40 | "drobeta_dungeon_s1f.smp", 41 | "drobeta_dungeon_v1f.smp", 42 | "drobeta_ne.smp", 43 | "drobeta_nw.smp", 44 | "drobeta_ox.smp", 45 | "drobeta_se.smp", 46 | "drobeta_stadium.smp", 47 | "drobeta_sw.smp", 48 | "eslania_dungeon.smp", 49 | "eslania_ne.smp", 50 | "eslania_nw.smp", 51 | "eslania_se.smp", 52 | "eslania_sw.smp", 53 | "freepk.smp", 54 | "gate_of_alter.smp", 55 | "gdr_illusion_01.smp", 56 | "gdr_illusion_02.smp", 57 | "gdr_lair_01.smp", 58 | "gdr_lair_hard.smp", 59 | "guild_army_1f.smp", 60 | "guild_army_2f.smp", 61 | "guild_army_3f.smp", 62 | "guild_army_4f.smp", 63 | "guild_army_b1.smp", 64 | "guild_cleric_1f.smp", 65 | "guild_cleric_2f.smp", 66 | "guild_cleric_3f.smp", 67 | "guild_cleric_4f.smp", 68 | "guild_cleric_b1.smp", 69 | "guild_knight_1f.smp", 70 | "guild_knight_2f.smp", 71 | "guild_knight_3f.smp", 72 | "guild_knight_4f.smp", 73 | "guild_knight_b1.smp", 74 | "hexserius_dungeon1f.smp", 75 | "hexserius_dungeon2f.smp", 76 | "icen_dungeon3f.smp", 77 | "ik_lab.smp", 78 | "ik_lab_b1f.smp", 79 | "ik_lab_b2f.smp", 80 | "ik_offic.smp", 81 | "kali_cave.smp", 82 | "laom_dungeon3f.smp", 83 | "laom_dungeon4f.smp", 84 | "laom_dungeon5f.smp", 85 | "limbo_dungeon.smp", 86 | "limbo_lair_ne.smp", 87 | "limbo_lair_nw.smp", 88 | "limbo_lair_se.smp", 89 | "limbo_lair_sw.smp", 90 | "losttaiyan_b1f.smp", 91 | "losttaiyan_b2f.smp", 92 | "losttower_1f.smp", 93 | "losttower_2f.smp", 94 | "lusttower_1f.smp", 95 | "lusttower_2f.smp", 96 | "maze.smp", 97 | "octavus_dungeon1f.smp", 98 | "octavus_dungeon2f.smp", 99 | "ousters_dungeon01.smp", 100 | "ousters_dungeon02.smp", 101 | "ousters_dungeon03.smp", 102 | "ousters_dungeon04.smp", 103 | "ousters_village.smp", 104 | "path_to_fears.smp", 105 | "pentanus_dungeon1f.smp", 106 | "pentanus_dungeon2f.smp", 107 | "perona_ne.smp", 108 | "perona_nw.smp", 109 | "perona_se.smp", 110 | "perona_sw.smp", 111 | "quartus_dungeon1f.smp", 112 | "quartus_dungeon2f.smp", 113 | "rasen_battlezone.smp", 114 | "rasen_training.smp", 115 | "rasen_yard.smp", 116 | "rodin_ne.smp", 117 | "rodin_nw.smp", 118 | "rodin_se.smp", 119 | "rodin_sw.smp", 120 | "septimus_dungeon1f.smp", 121 | "septimus_dungeon2f.smp", 122 | "siege_warfare.smp", 123 | "slayer_battlezone1.smp", 124 | "slayer_battlezone2.smp", 125 | "slayerpk.smp", 126 | "slayers_training.smp", 127 | "survival.smp", 128 | "team_hdqrs.smp", 129 | "tepes_lair.smp", 130 | "tepes_lair_clon.smp", 131 | "tertius_dungeon1f.smp", 132 | "tertius_dungeon2f.smp", 133 | "timore_ne.smp", 134 | "timore_nw.smp", 135 | "timore_se.smp", 136 | "timore_sw.smp", 137 | "trapzone01.smp", 138 | "trapzone02.smp", 139 | "tunnel_ghorgova.smp", 140 | "tunnel_peiac.smp", 141 | "tutorial_n.smp", 142 | "tutorial_s.smp", 143 | "under_pass_1f.smp", 144 | "under_pass_2f.smp", 145 | "vampire_village.smp", 146 | "vampirepk.smp", 147 | "vranco_ne.smp", 148 | "vranco_nw.smp", 149 | "vranco_se.smp", 150 | "vranco_sw.smp", 151 | } 152 | 153 | for _, v := range files { 154 | smp, err := ReadSMP(v) 155 | if err != nil { 156 | t.Fatal(err) 157 | } 158 | if smp == nil { 159 | t.Fatalf("err不为空,smp却为空:%s\n", v) 160 | } 161 | // t.Logf("name: %s, id: %d\n", v, smp.ZoneID) 162 | // t.Logf("%#v\n", smp) 163 | if smp.Width == 0 || smp.Height == 0 { 164 | t.Fatal("这个地图不对:%s\n", v) 165 | } 166 | } 167 | } 168 | 169 | func TestReadSSI(t *testing.T) { 170 | files := []string{ 171 | "adam_c.ssi", 172 | "adam_e.ssi", 173 | "adam_new_c.ssi", 174 | "adam_new_c_n.ssi", 175 | "adam_new_c_s.ssi", 176 | "adam_new_e.ssi", 177 | "adam_new_w.ssi", 178 | "adam_w.ssi", 179 | "alter_of_blood.ssi", 180 | "asylion_dungeon.ssi", 181 | "bathory_battlezone.ssi", 182 | "bathory_dungeon_b1f.ssi", 183 | "bathory_dungeon_b2f.ssi", 184 | "bathory_dungeon_b3f.ssi", 185 | "bathory_dungeon_b4f.ssi", 186 | "bathory_lair.ssi", 187 | "bathory_lair_clon.ssi", 188 | "caligo_dungeon.ssi", 189 | "castalo_ne.ssi", 190 | "castellum.ssi", 191 | "castle_hexserius.ssi", 192 | "castle_octavus.ssi", 193 | "castle_pentanus.ssi", 194 | "castle_quartus.ssi", 195 | "castle_rasen_1_1.ssi", 196 | "castle_rasen_1_2.ssi", 197 | "castle_rasen_2_2.ssi", 198 | "castle_septimus.ssi", 199 | "castle_tertius.ssi", 200 | "clan_hdqrs.ssi", 201 | "devt.ssi", 202 | "drobeta_dungeon_s1f.ssi", 203 | "drobeta_dungeon_v1f.ssi", 204 | "drobeta_ne.ssi", 205 | "drobeta_nw.ssi", 206 | "drobeta_ox.ssi", 207 | "drobeta_se.ssi", 208 | "drobeta_stadium.ssi", 209 | "drobeta_sw.ssi", 210 | "eslania_dungeon.ssi", 211 | "eslania_ne.ssi", 212 | "eslania_nw.ssi", 213 | "eslania_se.ssi", 214 | "eslania_sw.ssi", 215 | "freepk.ssi", 216 | "gate_of_alter.ssi", 217 | "gdr_illusion_01.ssi", 218 | "gdr_illusion_02.ssi", 219 | "gdr_lair_01.ssi", 220 | "gdr_lair_hard.ssi", 221 | "guild_army_1f.ssi", 222 | "guild_army_2f.ssi", 223 | "guild_army_3f.ssi", 224 | "guild_army_4f.ssi", 225 | "guild_army_b1.ssi", 226 | "guild_cleric_1f.ssi", 227 | "guild_cleric_2f.ssi", 228 | "guild_cleric_3f.ssi", 229 | "guild_cleric_4f.ssi", 230 | "guild_cleric_b1.ssi", 231 | "guild_knight_1f.ssi", 232 | "guild_knight_2f.ssi", 233 | "guild_knight_3f.ssi", 234 | "guild_knight_4f.ssi", 235 | "guild_knight_b1.ssi", 236 | "hexserius_dungeon1f.ssi", 237 | "hexserius_dungeon2f.ssi", 238 | "icen_dungeon3f.ssi", 239 | "ik_lab.ssi", 240 | "ik_lab_b1f.ssi", 241 | "ik_lab_b2f.ssi", 242 | "ik_offic.ssi", 243 | "kali_cave.ssi", 244 | "laom_dungeon3f.ssi", 245 | "laom_dungeon4f.ssi", 246 | "laom_dungeon5f.ssi", 247 | "limbo_dungeon.ssi", 248 | "limbo_lair_ne.ssi", 249 | "limbo_lair_nw.ssi", 250 | "limbo_lair_se.ssi", 251 | "limbo_lair_sw.ssi", 252 | "losttaiyan_b1f.ssi", 253 | "losttaiyan_b2f.ssi", 254 | "losttower_1f.ssi", 255 | "losttower_2f.ssi", 256 | "lusttower_1f.ssi", 257 | "lusttower_2f.ssi", 258 | "maze.ssi", 259 | "octavus_dungeon1f.ssi", 260 | "octavus_dungeon2f.ssi", 261 | "ousters_dungeon01.ssi", 262 | "ousters_dungeon02.ssi", 263 | "ousters_dungeon03.ssi", 264 | "ousters_dungeon04.ssi", 265 | "ousters_village.ssi", 266 | "path_to_fears.ssi", 267 | "pentanus_dungeon1f.ssi", 268 | "pentanus_dungeon2f.ssi", 269 | "perona_ne.ssi", 270 | "perona_nw.ssi", 271 | "perona_se.ssi", 272 | "perona_sw.ssi", 273 | "quartus_dungeon1f.ssi", 274 | "quartus_dungeon2f.ssi", 275 | "rasen_battlezone.ssi", 276 | "rasen_training.ssi", 277 | "rasen_yard.ssi", 278 | "rodin_ne.ssi", 279 | "rodin_nw.ssi", 280 | "rodin_se.ssi", 281 | "rodin_sw.ssi", 282 | "septimus_dungeon1f.ssi", 283 | "septimus_dungeon2f.ssi", 284 | "siege_warfare.ssi", 285 | "slayer_battlezone1.ssi", 286 | "slayer_battlezone2.ssi", 287 | "slayerpk.ssi", 288 | "slayers_training.ssi", 289 | "survival.ssi", 290 | "team_hdqrs.ssi", 291 | "tepes_lair.ssi", 292 | "tepes_lair_clon.ssi", 293 | "tertius_dungeon1f.ssi", 294 | "tertius_dungeon2f.ssi", 295 | "timore_ne.ssi", 296 | "timore_nw.ssi", 297 | "timore_se.ssi", 298 | "timore_sw.ssi", 299 | "trapzone01.ssi", 300 | "trapzone02.ssi", 301 | "tunnel_ghorgova.ssi", 302 | "tunnel_peiac.ssi", 303 | "tutorial_n.ssi", 304 | "tutorial_s.ssi", 305 | "under_pass_1f.ssi", 306 | "under_pass_2f.ssi", 307 | "vampire_village.ssi", 308 | "vampirepk.ssi", 309 | "vranco_ne.ssi", 310 | "vranco_nw.ssi", 311 | "vranco_se.ssi", 312 | "vranco_sw.ssi", 313 | } 314 | 315 | for _, v := range files { 316 | _, err := ReadSSI(v) 317 | if err != nil { 318 | t.Fatal(err) 319 | } 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /data/subiteminfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/binary" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "io" 7 | ) 8 | 9 | type SubItemInfo struct { 10 | ObjectID ObjectID_t 11 | IClass byte 12 | ItemType ItemType_t 13 | ItemNum ItemNum_t 14 | SlotID SlotID_t 15 | } 16 | 17 | func (info *SubItemInfo) Size() uint32 { 18 | return 9 19 | } 20 | 21 | func (info *SubItemInfo) Read(reader io.Reader) error { 22 | binary.Read(reader, binary.LittleEndian, &info.ObjectID) 23 | binary.Read(reader, binary.LittleEndian, &info.IClass) 24 | binary.Read(reader, binary.LittleEndian, &info.ItemType) 25 | binary.Read(reader, binary.LittleEndian, &info.ItemNum) 26 | binary.Read(reader, binary.LittleEndian, &info.SlotID) 27 | return nil 28 | } 29 | 30 | func (info *SubItemInfo) Write(writer io.Writer) error { 31 | binary.Write(writer, binary.LittleEndian, &info.ObjectID) 32 | binary.Write(writer, binary.LittleEndian, &info.IClass) 33 | binary.Write(writer, binary.LittleEndian, &info.ItemType) 34 | binary.Write(writer, binary.LittleEndian, &info.ItemNum) 35 | binary.Write(writer, binary.LittleEndian, &info.SlotID) 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /data/zoneinfo.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/tiancaiamao/ouster/config" 6 | // "github.com/tiancaiamao/ouster/log" 7 | . "github.com/tiancaiamao/ouster/util" 8 | "os" 9 | "path" 10 | ) 11 | 12 | type ZoneInfoItem struct { 13 | ZoneID ZoneID_t 14 | ZoneInfo ZoneInfo 15 | } 16 | 17 | type Monster struct { 18 | MonsterType MonsterType_t 19 | Count int 20 | } 21 | 22 | type ZoneInfo struct { 23 | EventMonsterList []Monster 24 | MonsterList []Monster 25 | } 26 | 27 | var ZoneInfoTable map[ZoneID_t]ZoneInfo 28 | 29 | func init() { 30 | var array []ZoneInfoItem 31 | 32 | filePath := path.Join(config.DataFilePath, "zoneinfo.json") 33 | file, err := os.Open(filePath) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | dec := json.NewDecoder(file) 39 | err = dec.Decode(&array) 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | ZoneInfoTable = make(map[ZoneID_t]ZoneInfo) 45 | for _, v := range array { 46 | ZoneInfoTable[v.ZoneID] = v.ZoneInfo 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /data/zoneinfo.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ZoneID": 62, 4 | "ZoneInfo": { 5 | "EventMonsterList": null, 6 | "MonsterList": [ 7 | { 8 | "MonsterType": 4, 9 | "Count": 20 10 | }, 11 | { 12 | "MonsterType": 29, 13 | "Count": 20 14 | }, 15 | { 16 | "MonsterType": 30, 17 | "Count": 20 18 | }, 19 | { 20 | "MonsterType": 31, 21 | "Count": 20 22 | }, 23 | { 24 | "MonsterType": 32, 25 | "Count": 20 26 | }, 27 | { 28 | "MonsterType": 33, 29 | "Count": 20 30 | }, 31 | { 32 | "MonsterType": 34, 33 | "Count": 20 34 | }, 35 | { 36 | "MonsterType": 35, 37 | "Count": 20 38 | }, 39 | { 40 | "MonsterType": 36, 41 | "Count": 20 42 | }, 43 | { 44 | "MonsterType": 37, 45 | "Count": 20 46 | }, 47 | { 48 | "MonsterType": 5, 49 | "Count": 8 50 | }, 51 | { 52 | "MonsterType": 38, 53 | "Count": 8 54 | }, 55 | { 56 | "MonsterType": 39, 57 | "Count": 8 58 | }, 59 | { 60 | "MonsterType": 40, 61 | "Count": 8 62 | }, 63 | { 64 | "MonsterType": 41, 65 | "Count": 8 66 | }, 67 | { 68 | "MonsterType": 42, 69 | "Count": 8 70 | }, 71 | { 72 | "MonsterType": 43, 73 | "Count": 8 74 | }, 75 | { 76 | "MonsterType": 44, 77 | "Count": 8 78 | }, 79 | { 80 | "MonsterType": 45, 81 | "Count": 8 82 | }, 83 | { 84 | "MonsterType": 46, 85 | "Count": 8 86 | }, 87 | { 88 | "MonsterType": 7, 89 | "Count": 6 90 | }, 91 | { 92 | "MonsterType": 56, 93 | "Count": 6 94 | }, 95 | { 96 | "MonsterType": 57, 97 | "Count": 6 98 | }, 99 | { 100 | "MonsterType": 58, 101 | "Count": 6 102 | }, 103 | { 104 | "MonsterType": 59, 105 | "Count": 6 106 | }, 107 | { 108 | "MonsterType": 60, 109 | "Count": 6 110 | }, 111 | { 112 | "MonsterType": 61, 113 | "Count": 6 114 | }, 115 | { 116 | "MonsterType": 62, 117 | "Count": 6 118 | }, 119 | { 120 | "MonsterType": 63, 121 | "Count": 6 122 | }, 123 | { 124 | "MonsterType": 64, 125 | "Count": 6 126 | }, 127 | { 128 | "MonsterType": 6, 129 | "Count": 3 130 | }, 131 | { 132 | "MonsterType": 47, 133 | "Count": 3 134 | }, 135 | { 136 | "MonsterType": 48, 137 | "Count": 3 138 | }, 139 | { 140 | "MonsterType": 49, 141 | "Count": 3 142 | }, 143 | { 144 | "MonsterType": 50, 145 | "Count": 3 146 | }, 147 | { 148 | "MonsterType": 51, 149 | "Count": 3 150 | }, 151 | { 152 | "MonsterType": 52, 153 | "Count": 3 154 | }, 155 | { 156 | "MonsterType": 53, 157 | "Count": 3 158 | }, 159 | { 160 | "MonsterType": 54, 161 | "Count": 3 162 | }, 163 | { 164 | "MonsterType": 55, 165 | "Count": 3 166 | } 167 | ] 168 | } 169 | }, 170 | { 171 | "ZoneID": 63, 172 | "ZoneInfo": { 173 | "EventMonsterList": null, 174 | "MonsterList": [ 175 | { 176 | "MonsterType": 5, 177 | "Count": 18 178 | }, 179 | { 180 | "MonsterType": 38, 181 | "Count": 18 182 | }, 183 | { 184 | "MonsterType": 39, 185 | "Count": 18 186 | }, 187 | { 188 | "MonsterType": 40, 189 | "Count": 18 190 | }, 191 | { 192 | "MonsterType": 41, 193 | "Count": 18 194 | }, 195 | { 196 | "MonsterType": 42, 197 | "Count": 18 198 | }, 199 | { 200 | "MonsterType": 43, 201 | "Count": 18 202 | }, 203 | { 204 | "MonsterType": 44, 205 | "Count": 18 206 | }, 207 | { 208 | "MonsterType": 45, 209 | "Count": 18 210 | }, 211 | { 212 | "MonsterType": 46, 213 | "Count": 18 214 | }, 215 | { 216 | "MonsterType": 7, 217 | "Count": 11 218 | }, 219 | { 220 | "MonsterType": 56, 221 | "Count": 11 222 | }, 223 | { 224 | "MonsterType": 57, 225 | "Count": 11 226 | }, 227 | { 228 | "MonsterType": 58, 229 | "Count": 11 230 | }, 231 | { 232 | "MonsterType": 59, 233 | "Count": 11 234 | }, 235 | { 236 | "MonsterType": 60, 237 | "Count": 10 238 | }, 239 | { 240 | "MonsterType": 61, 241 | "Count": 10 242 | }, 243 | { 244 | "MonsterType": 62, 245 | "Count": 10 246 | }, 247 | { 248 | "MonsterType": 63, 249 | "Count": 10 250 | }, 251 | { 252 | "MonsterType": 64, 253 | "Count": 10 254 | }, 255 | { 256 | "MonsterType": 6, 257 | "Count": 3 258 | }, 259 | { 260 | "MonsterType": 47, 261 | "Count": 3 262 | }, 263 | { 264 | "MonsterType": 48, 265 | "Count": 3 266 | }, 267 | { 268 | "MonsterType": 49, 269 | "Count": 3 270 | }, 271 | { 272 | "MonsterType": 50, 273 | "Count": 3 274 | }, 275 | { 276 | "MonsterType": 51, 277 | "Count": 3 278 | }, 279 | { 280 | "MonsterType": 52, 281 | "Count": 3 282 | }, 283 | { 284 | "MonsterType": 53, 285 | "Count": 3 286 | }, 287 | { 288 | "MonsterType": 54, 289 | "Count": 3 290 | }, 291 | { 292 | "MonsterType": 55, 293 | "Count": 3 294 | }, 295 | { 296 | "MonsterType": 9, 297 | "Count": 2 298 | }, 299 | { 300 | "MonsterType": 74, 301 | "Count": 2 302 | }, 303 | { 304 | "MonsterType": 75, 305 | "Count": 2 306 | }, 307 | { 308 | "MonsterType": 76, 309 | "Count": 2 310 | }, 311 | { 312 | "MonsterType": 77, 313 | "Count": 2 314 | }, 315 | { 316 | "MonsterType": 78, 317 | "Count": 1 318 | }, 319 | { 320 | "MonsterType": 79, 321 | "Count": 1 322 | }, 323 | { 324 | "MonsterType": 80, 325 | "Count": 1 326 | }, 327 | { 328 | "MonsterType": 81, 329 | "Count": 1 330 | }, 331 | { 332 | "MonsterType": 82, 333 | "Count": 1 334 | }, 335 | { 336 | "MonsterType": 687, 337 | "Count": 4 338 | } 339 | ] 340 | } 341 | } 342 | ] -------------------------------------------------------------------------------- /gameserver/agent.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/tiancaiamao/ouster/config" 6 | "github.com/tiancaiamao/ouster/log" 7 | "github.com/tiancaiamao/ouster/packet" 8 | . "github.com/tiancaiamao/ouster/util" 9 | "net" 10 | "os" 11 | "path" 12 | "time" 13 | ) 14 | 15 | type Agent struct { 16 | PlayerCreatureInterface 17 | Player 18 | 19 | scene chan<- AgentMessage 20 | computation chan func() 21 | } 22 | 23 | func NewAgent(conn net.Conn) *Agent { 24 | agent := new(Agent) 25 | InitPlayer(&agent.Player, conn) 26 | agent.computation = make(chan func(), 10) 27 | return agent 28 | } 29 | 30 | func (agent *Agent) Loop() { 31 | heartbeat := time.Tick(200 * time.Millisecond) 32 | for { 33 | select { 34 | case msg, ok := <-agent.client: 35 | if !ok { 36 | log.Debugln("客户端关了") 37 | return 38 | } 39 | log.Debugln("agent收到一个packet:", msg) 40 | agent.handleClientMessage(msg) 41 | case <-heartbeat: 42 | if agent.PlayerCreatureInterface != nil { 43 | agent.heartbeat() 44 | } 45 | case f, _ := <-agent.computation: 46 | f() 47 | } 48 | } 49 | } 50 | 51 | func (agent *Agent) handleClientMessage(pkt packet.Packet) { 52 | if pkt == nil { 53 | log.Errorln("不应该呀 怎么可能返回一个空") 54 | } 55 | handler, ok := packetHandlers[pkt.PacketID()] 56 | if !ok { 57 | log.Errorln("packet的handler未实现:", pkt.PacketID()) 58 | return 59 | } 60 | 61 | handler(pkt, agent) 62 | } 63 | 64 | // called in scene 65 | func (this *Player) handleAoiMessage(id uint32) { 66 | // obj := this.Scene.objects[id] 67 | // if _, ok := obj.(*Monster); ok { 68 | // log.Println("it's a monster...send message") 69 | // monster := obj.(*Monster) 70 | // if _, ok := this.nearby[id]; !ok { 71 | // this.nearby[id] = struct{}{} 72 | // 73 | // addMonster := &packet.GCAddMonster{ 74 | // ObjectID: uint32(id), 75 | // MonsterType: monster.MonsterType, 76 | // MonsterName: "test", 77 | // X: monster.X(), 78 | // Y: monster.Y(), 79 | // Dir: 2, 80 | // CurrentHP: monster.HP[ATTR_CURRENT], 81 | // MaxHP: monster.MaxHP(), 82 | // } 83 | // 84 | // this.send <- addMonster 85 | // monster.flag |= flagActive 86 | // log.Println("monster ", id, "set to active", monster.flag) 87 | // monster.Enemies = append(monster.Enemies, this.PacketID()) 88 | // } else { 89 | // 90 | // } 91 | // } 92 | } 93 | 94 | func (agent *Agent) save() error { 95 | fileName := path.Join(config.DataDir, agent.PlayerCreatureInstance().Name) 96 | f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 97 | if err != nil { 98 | return err 99 | } 100 | defer f.Close() 101 | 102 | enc := json.NewEncoder(f) 103 | info := agent.PCInfo() 104 | err = enc.Encode(info) 105 | if err != nil { 106 | return err 107 | } 108 | return nil 109 | } 110 | 111 | func (agent *Agent) ErrorClose() { 112 | agent.conn.Close() 113 | return 114 | } 115 | 116 | func decreaseConsumeMP(agent *Agent) int { 117 | // TODO 118 | return 0 119 | } 120 | 121 | func decreaseMana(agent *Agent, mana int) { 122 | // TODO 123 | } 124 | 125 | func (agent *Agent) hasRankBonus() bool { 126 | // TODO 127 | return false 128 | } 129 | 130 | func hasEnoughMana(agent *Agent, requireMP int) bool { 131 | // TODO 132 | return true 133 | } 134 | 135 | func (agent *Agent) NearbyAgent(id ObjectID_t) *Agent { 136 | return nil 137 | } 138 | -------------------------------------------------------------------------------- /gameserver/bitset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type BitSet struct { 4 | data []uint64 5 | size uint 6 | } 7 | 8 | func NewBitSet(sz uint) BitSet { 9 | length := (sz / 64) + 1 10 | return BitSet{ 11 | data: make([]uint64, length), 12 | size: sz, 13 | } 14 | } 15 | 16 | func (bs BitSet) IsFlag(ith uint) bool { 17 | idx := ith / 64 18 | off := ith % 64 19 | 20 | return (bs.data[idx] & (1 << off)) != 0 21 | } 22 | 23 | func (bs BitSet) IsDead(ith uint) bool { 24 | idx := ith / 64 25 | off := ith % 64 26 | 27 | return (bs.data[idx] & (1 << off)) == 0 28 | } 29 | -------------------------------------------------------------------------------- /gameserver/corpse.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | type CorpseType uint8 8 | 9 | const ( 10 | SLAYER_CORPSE = iota 11 | VAMPIRE_CORPSE 12 | NPC_CORPSE 13 | MONSTER_CORPSE 14 | OUSTERS_CORPSE 15 | ) 16 | 17 | type Corpse struct { 18 | Item 19 | 20 | Treasures []*Item 21 | TreasureCount uint8 22 | 23 | X ZoneCoord_t 24 | Y ZoneCoord_t 25 | } 26 | 27 | // Corpse继承Item对象,实现ItemInterface接口 28 | func (c *Corpse) ItemClass() ItemClass { 29 | return ITEM_CLASS_CORPSE 30 | } 31 | 32 | type SlayerCorpse struct { 33 | Corpse 34 | } 35 | 36 | type OusterCorpse struct { 37 | Corpse 38 | } 39 | 40 | type VampireCorpse struct { 41 | Corpse 42 | } 43 | 44 | type MonsterCorpse struct { 45 | Corpse 46 | MonsterType MonsterType_t 47 | Name string 48 | HasHead bool 49 | HostName string 50 | HostPartyID int 51 | QuestHostName string 52 | LastKiller ObjectID_t 53 | } 54 | -------------------------------------------------------------------------------- /gameserver/creature.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | type CreatureClass int 8 | 9 | const ( 10 | CREATURE_CLASS_SLAYER CreatureClass = iota // PC Slayer 11 | CREATURE_CLASS_VAMPIRE // PC Vampire 12 | CREATURE_CLASS_NPC // NPC 13 | CREATURE_CLASS_MONSTER // NPC Slayer, NPC Vampire 14 | CREATURE_CLASS_OUSTER // PC Ousters 15 | CREATURE_CLASS_MAX 16 | ) 17 | 18 | type CreatureInterface interface { 19 | ObjectInterface 20 | CreatureClass() CreatureClass 21 | CreatureInstance() *Creature 22 | 23 | getProtection() Protection_t 24 | getHP(int) HP_t 25 | IsAbleToMove() bool 26 | } 27 | 28 | type Creature struct { 29 | Object 30 | 31 | Scene *Scene 32 | MoveMode MoveMode 33 | X ZoneCoord_t 34 | Y ZoneCoord_t 35 | Dir Dir_t 36 | 37 | Level Level_t 38 | 39 | ViewportWidth ZoneCoord_t 40 | ViewportUpperHeight ZoneCoord_t 41 | ViewportLowerHeight ZoneCoord_t 42 | Resist [MAGIC_DOMAIN_MAX]Resist_t 43 | 44 | Flag BitSet 45 | 46 | Sight Sight_t 47 | } 48 | 49 | func (c *Creature) Init() { 50 | c.Flag = NewBitSet(EFFECT_CLASS_MAX) 51 | } 52 | 53 | func (c *Creature) ObjectClass() ObjectClass { 54 | return OBJECT_CLASS_CREATURE 55 | } 56 | 57 | func (c *Creature) CreatureInstance() *Creature { 58 | return c 59 | } 60 | 61 | // 派生类中重写这个函数 62 | func (c *Creature) heartbeat() {} 63 | 64 | // TODO 65 | func (c *Creature) IsAbleToMove() bool { 66 | if c.Flag.IsFlag(EFFECT_CLASS_COMA) || 67 | c.Flag.IsFlag(EFFECT_CLASS_PARALYZE) || 68 | c.Flag.IsFlag(EFFECT_CLASS_ETERNITY_PAUSE) || 69 | c.Flag.IsFlag(EFFECT_CLASS_CASKET) || 70 | c.Flag.IsFlag(EFFECT_CLASS_CAUSE_CRITICAL_WOUNDS) || 71 | c.Flag.IsFlag(EFFECT_CLASS_SOUL_CHAIN) || 72 | c.Flag.IsFlag(EFFECT_CLASS_LOVE_CHAIN) || 73 | c.Flag.IsFlag(EFFECT_CLASS_GUN_SHOT_GUIDANCE_AIM) || 74 | c.Flag.IsFlag(EFFECT_CLASS_SLEEP) || 75 | c.Flag.IsFlag(EFFECT_CLASS_ARMAGEDDON) || 76 | c.Flag.IsFlag(EFFECT_CLASS_POISON_MESH) || 77 | c.Flag.IsFlag(EFFECT_CLASS_TENDRIL) || 78 | c.Flag.IsFlag(EFFECT_CLASS_TRAPPED) || 79 | c.Flag.IsFlag(EFFECT_CLASS_INSTALL_TURRET) || 80 | c.Flag.IsFlag(EFFECT_CLASS_EXPLOSION_WATER) { 81 | return false 82 | } 83 | 84 | return true 85 | } 86 | 87 | func (c *Creature) IsFlag(effect uint) bool { 88 | return c.Flag.IsFlag(effect) 89 | } 90 | 91 | func (c *Creature) isFlag(effect uint) bool { 92 | return c.Flag.IsFlag(effect) 93 | } 94 | 95 | // TODO 96 | func (c *Creature) removeFlag(effect EffectClass) { 97 | 98 | } 99 | 100 | func (c *Creature) setFlag(ec EffectClass) { 101 | 102 | } 103 | 104 | func canSee(watcher CreatureInterface, marker CreatureInterface) bool { 105 | return true //TODO 106 | } 107 | 108 | func (c *Creature) canMove(nx ZoneCoord_t, ny ZoneCoord_t) bool { 109 | if c.Flag.IsFlag(EFFECT_CLASS_POISON_MESH) || 110 | c.Flag.IsFlag(EFFECT_CLASS_TENDRIL) || 111 | c.Flag.IsFlag(EFFECT_CLASS_BLOODY_WALL_BLOCKED) || 112 | c.Flag.IsFlag(EFFECT_CLASS_CASKET) || 113 | !isValidZoneCoord(&c.Scene.Zone, nx, ny) { 114 | return false 115 | } 116 | 117 | rTile := c.Scene.getTile(nx, ny) 118 | 119 | if rTile.isBlocked(c.MoveMode) || 120 | rTile.hasEffect() && (rTile.getEffect(EFFECT_CLASS_BLOODY_WALL_BLOCKED) != nil || 121 | rTile.getEffect(EFFECT_CLASS_SANCTUARY) != nil) { 122 | return false 123 | } 124 | 125 | rNewTile := c.Scene.getTile(c.X, c.Y) 126 | 127 | if rNewTile.getEffect(EFFECT_CLASS_SANCTUARY) != nil { 128 | return false 129 | } 130 | 131 | return true 132 | } 133 | 134 | func (c *Creature) isBlockedByCreature(nx ZoneCoord_t, ny ZoneCoord_t) bool { 135 | if !isValidZoneCoord(&c.Scene.Zone, nx, ny) || 136 | !c.Scene.getTile(nx, ny).HasCreature(c.MoveMode) { 137 | return false 138 | } 139 | return true 140 | } 141 | 142 | func (c *Creature) addEffect(effect EffectInterface) { 143 | 144 | } 145 | 146 | // TODO 147 | func verifyDistance(c1, c2 CreatureInterface) bool { 148 | return true 149 | } 150 | -------------------------------------------------------------------------------- /gameserver/directive.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | DIRECTIVE_COND_ENEMY_RANGE_MELEE = iota 5 | DIRECTIVE_COND_ENEMY_RANGE_MISSILE 6 | DIRECTIVE_COND_ENEMY_RANGE_CLOSE 7 | DIRECTIVE_COND_ENEMY_RANGE_OUT_OF_SIGHT 8 | DIRECTIVE_COND_ENEMY_DYING 9 | DIRECTIVE_COND_ENEMY_NOT_BLOOD_DRAINED 10 | DIRECTIVE_COND_ENEMY_NOT_GREEN_POISONED 11 | DIRECTIVE_COND_ENEMY_NOT_YELLOW_POISONED 12 | DIRECTIVE_COND_ENEMY_NOT_DARKBLUE_POISONED 13 | DIRECTIVE_COND_ENEMY_NOT_GREEN_STALKERED 14 | DIRECTIVE_COND_ENEMY_NOT_PARALYZED 15 | DIRECTIVE_COND_ENEMY_NOT_DOOMED 16 | DIRECTIVE_COND_ENEMY_NOT_BLINDED 17 | DIRECTIVE_COND_ENEMY_NOT_IN_DARKNESS 18 | DIRECTIVE_COND_ENEMY_NOT_SEDUCTION 19 | DIRECTIVE_COND_IM_OK 20 | DIRECTIVE_COND_IM_DYING 21 | DIRECTIVE_COND_IM_DAMAGED 22 | DIRECTIVE_COND_IM_HIDING 23 | DIRECTIVE_COND_IM_WOLF 24 | DIRECTIVE_COND_IM_BAT 25 | DIRECTIVE_COND_IM_INVISIBLE 26 | DIRECTIVE_COND_IM_WALKING_WALL 27 | DIRECTIVE_COND_TIMING_BLOOD_DRAIN 28 | DIRECTIVE_COND_MASTER_SUMMON_TIMING 29 | DIRECTIVE_COND_MASTER_NOT_READY 30 | DIRECTIVE_COND_IM_IN_BAD_POSITION 31 | DIRECTIVE_COND_FIND_WEAK_ENEMY 32 | DIRECTIVE_COND_ENEMY_NOT_DEATH 33 | DIRECTIVE_COND_ENEMY_NOT_HALLUCINATION 34 | DIRECTIVE_COND_TIMING_MASTER_BLOOD_DRAIN 35 | DIRECTIVE_COND_TIMING_DUPLICATE_SELF 36 | DIRECTIVE_COND_ENEMY_RANGE_IN_MISSILE 37 | DIRECTIVE_COND_POSSIBLE_SUMMON_MONSTERS 38 | DIRECTIVE_COND_ENEMY_TILE_NOT_ACID_SWAMP 39 | DIRECTIVE_COND_ENEMY_ON_AIR 40 | DIRECTIVE_COND_ENEMY_ON_SAFE_ZONE 41 | DIRECTIVE_COND_CAN_ATTACK_THROWING_AXE 42 | DIRECTIVE_COND_MAX 43 | ) 44 | 45 | const ( 46 | DIRECTIVE_ACTION_APPROACH = iota 47 | DIRECTIVE_ACTION_FLEE 48 | DIRECTIVE_ACTION_USE_SKILL 49 | DIRECTIVE_ACTION_FORGET 50 | DIRECTIVE_ACTION_CHANGE_ENEMY 51 | DIRECTIVE_ACTION_MOVE_RANDOM 52 | DIRECTIVE_ACTION_WAIT 53 | DIRECTIVE_ACTION_FAST_FLEE 54 | DIRECTIVE_ACTION_SAY 55 | DIRECTIVE_ACTION_MAX 56 | ) 57 | 58 | type Directive struct { 59 | Conditions []int 60 | Action int 61 | Parameter int 62 | Ratio int 63 | Weight int 64 | } 65 | 66 | type DirectiveSet struct { 67 | Directives []*Directive 68 | DeadDirectives []*Directive 69 | Name string 70 | 71 | bAttackAir bool 72 | bSeeSafeZone bool 73 | } 74 | -------------------------------------------------------------------------------- /gameserver/item.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | type ItemClass int 8 | 9 | const ( 10 | ITEM_CLASS_MOTORCYCLE ItemClass = iota // 0 11 | ITEM_CLASS_POTION // 1 12 | ITEM_CLASS_WATER // 2 13 | ITEM_CLASS_HOLYWATER // 3 14 | ITEM_CLASS_MAGAZINE // 4 15 | ITEM_CLASS_BOMB_MATERIAL // 5 16 | ITEM_CLASS_ETC // 6 17 | ITEM_CLASS_KEY // 7 18 | ITEM_CLASS_RING // 8 19 | ITEM_CLASS_BRACELET // 9 20 | ITEM_CLASS_NECKLACE // 10 21 | ITEM_CLASS_COAT // 11 22 | ITEM_CLASS_TROUSER // 12 23 | ITEM_CLASS_SHOES // 13 24 | ITEM_CLASS_SWORD // 14 25 | ITEM_CLASS_BLADE // 15 26 | ITEM_CLASS_SHIELD // 16 27 | ITEM_CLASS_CROSS // 17 28 | ITEM_CLASS_GLOVE // 18 29 | ITEM_CLASS_HELM // 19 30 | ITEM_CLASS_SG // 20 31 | ITEM_CLASS_SMG // 21 32 | ITEM_CLASS_AR // 22 33 | ITEM_CLASS_SR // 23 34 | ITEM_CLASS_BOMB // 24 35 | ITEM_CLASS_MINE // 25 36 | ITEM_CLASS_BELT // 26 37 | ITEM_CLASS_LEARNINGITEM // 27 38 | ITEM_CLASS_MONEY // 28 39 | ITEM_CLASS_CORPSE // 29 40 | ITEM_CLASS_VAMPIRE_RING // 30 41 | ITEM_CLASS_VAMPIRE_BRACELET // 31 42 | ITEM_CLASS_VAMPIRE_NECKLACE // 32 43 | ITEM_CLASS_VAMPIRE_COAT // 33 44 | ITEM_CLASS_SKULL // 34 45 | ITEM_CLASS_MACE // 35 46 | ITEM_CLASS_SERUM // 36 47 | ITEM_CLASS_VAMPIRE_ETC // 37 48 | ITEM_CLASS_SLAYER_PORTAL_ITEM // 38 49 | ITEM_CLASS_VAMPIRE_PORTAL_ITEM // 39 50 | ITEM_CLASS_EVENT_GIFT_BOX // 40 51 | ITEM_CLASS_EVENT_STAR // 41 52 | ITEM_CLASS_VAMPIRE_EARRING // 42 53 | ITEM_CLASS_RELIC // 43 54 | ITEM_CLASS_VAMPIRE_WEAPON // 44 55 | ITEM_CLASS_VAMPIRE_AMULET // 45 56 | ITEM_CLASS_QUEST_ITEM // 46 57 | ITEM_CLASS_EVENT_TREE // 47 58 | ITEM_CLASS_EVENT_ETC // 48 59 | ITEM_CLASS_BLOOD_BIBLE // 49 60 | ITEM_CLASS_CASTLE_SYMBOL // 50 61 | ITEM_CLASS_COUPLE_RING // 51 62 | ITEM_CLASS_VAMPIRE_COUPLE_RING // 52 63 | ITEM_CLASS_EVENT_ITEM // 53 64 | ITEM_CLASS_DYE_POTION // 54 65 | ITEM_CLASS_RESURRECT_ITEM // 55 66 | ITEM_CLASS_MIXING_ITEM // 56 67 | ITEM_CLASS_OUSTERS_ARMSBAND // 57 68 | ITEM_CLASS_OUSTERS_BOOTS // 58 69 | ITEM_CLASS_OUSTERS_CHAKRAM // 59 70 | ITEM_CLASS_OUSTERS_CIRCLET // 60 71 | ITEM_CLASS_OUSTERS_COAT // 61 72 | ITEM_CLASS_OUSTERS_PENDENT // 62 73 | ITEM_CLASS_OUSTERS_RING // 63 74 | ITEM_CLASS_OUSTERS_STONE // 64 75 | ITEM_CLASS_OUSTERS_WRISTLET // 65 76 | ITEM_CLASS_LARVA // 66 77 | ITEM_CLASS_PUPA // 67 78 | ITEM_CLASS_COMPOS_MEI // 68 79 | ITEM_CLASS_OUSTERS_SUMMON_ITEM // 69 80 | ITEM_CLASS_EFFECT_ITEM // 70 81 | ITEM_CLASS_CODE_SHEET // 71 82 | ITEM_CLASS_MOON_CARD // 72 83 | ITEM_CLASS_SWEEPER // 73 84 | ITEM_CLASS_PET_ITEM // 74 85 | ITEM_CLASS_PET_FOOD // 75 86 | ITEM_CLASS_PET_ENCHANT_ITEM // 76 87 | ITEM_CLASS_LUCKY_BAG // 77 88 | ITEM_CLASS_SMS_ITEM // 78 89 | ITEM_CLASS_CORE_ZAP // 79 90 | ITEM_CLASS_GQUEST_ITEM // 80 91 | ITEM_CLASS_TRAP_ITEM // 81 92 | ITEM_CLASS_BLOOD_BIBLE_SIGN // 82 93 | ITEM_CLASS_WAR_ITEM // 83 94 | ITEM_CLASS_CARRYING_RECEIVER // 84 95 | ITEM_CLASS_SHOULDER_ARMOR // 85 96 | ITEM_CLASS_DERMIS // 86 97 | ITEM_CLASS_PERSONA // 87 98 | ITEM_CLASS_FASCIA // 88 99 | ITEM_CLASS_MITTEN // 89 100 | ITEM_CLASS_SUB_INVENTORY // 90 101 | ITEM_CLASS_MAX // 91 102 | ) 103 | 104 | type CreateType uint8 105 | 106 | const ( 107 | CREATE_TYPE_NORMAL = iota 108 | CREATE_TYPE_MONSTER 109 | CREATE_TYPE_SHOP 110 | CREATE_TYPE_GAMBLE 111 | CREATE_TYPE_ENCHANT 112 | CREATE_TYPE_GAME 113 | CREATE_TYPE_CREATE 114 | CREATE_TYPE_MALL 115 | CREATE_TYPE_PRIZE 116 | CREATE_TYPE_MIXING 117 | CREATE_TYPE_SPECIAL 118 | CREATE_TYPE_TIME_EXTENSION 119 | 120 | CREATE_TYPE_MAX 121 | ) 122 | 123 | // 派生类中,继承Item对象,实现ItemInterface接口 124 | 125 | type ItemInterface interface { 126 | ObjectInterface 127 | ItemInstance() *Item 128 | 129 | ItemClass() ItemClass 130 | } 131 | 132 | type Item struct { 133 | Object // item继承自object对象 134 | 135 | ItemID ItemID_t 136 | CreateType CreateType 137 | TimeLimit bool 138 | Hour int 139 | } 140 | 141 | // item实现了Object接口 142 | func (item *Item) ObjectClass() ObjectClass { 143 | return OBJECT_CLASS_ITEM 144 | } 145 | 146 | func (item *Item) ItemInstance() *Item { 147 | return item 148 | } 149 | -------------------------------------------------------------------------------- /gameserver/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/config" 5 | "github.com/tiancaiamao/ouster/log" 6 | "net" 7 | ) 8 | 9 | func main() { 10 | log.Infoln("Starting the server.") 11 | 12 | Initialize() 13 | 14 | listener, err := net.Listen("tcp", config.GameServerPort) 15 | checkError(err) 16 | 17 | log.Infoln("Game Server OK.") 18 | 19 | for { 20 | conn, err := listener.Accept() 21 | if err == nil { 22 | go handleClient(conn) 23 | } 24 | } 25 | } 26 | 27 | func checkError(err error) { 28 | if err != nil { 29 | panic(err) 30 | } 31 | } 32 | 33 | func handleClient(conn net.Conn) { 34 | log.Debug("accept a connection...") 35 | 36 | agent := NewAgent(conn) 37 | go agent.Loop() 38 | } 39 | -------------------------------------------------------------------------------- /gameserver/message.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/packet" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "sync" 7 | ) 8 | 9 | // 传interface相当于传指针,消息更小 10 | // 增加了解包的代价 11 | type AgentMessage interface { 12 | Sender() *Agent 13 | } 14 | 15 | func (agent *Agent) Sender() *Agent { 16 | return agent 17 | } 18 | 19 | type MoveMessage struct { 20 | *Agent 21 | *packet.CGMovePacket 22 | } 23 | 24 | type FastMoveMessage struct { 25 | *Agent 26 | X ZoneCoord_t 27 | Y ZoneCoord_t 28 | SkillType SkillType_t 29 | } 30 | 31 | type DamageMessage struct { 32 | *Agent 33 | target CreatureInterface 34 | damage Damage_t 35 | critical bool 36 | } 37 | 38 | type LoginMessage struct { 39 | *Agent 40 | wg *sync.WaitGroup 41 | } 42 | 43 | type LogoutMessage struct { 44 | *Agent 45 | // wg *sync.WaitGroup 46 | } 47 | 48 | type SkillBroadcastMessage struct { 49 | *Agent 50 | packet.Packet 51 | } 52 | 53 | type MeteorStrikeMessage struct { 54 | *Agent 55 | EffectMeteorStrike 56 | X uint8 57 | Y uint8 58 | } 59 | -------------------------------------------------------------------------------- /gameserver/npc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | type NPC struct { 8 | Creature 9 | } 10 | 11 | func (npc *NPC) CreatureClass() CreatureClass { 12 | return CREATURE_CLASS_NPC 13 | } 14 | 15 | type NPCManager struct { 16 | } 17 | 18 | func NewNPCManager() *NPCManager { 19 | return &NPCManager{} 20 | } 21 | 22 | func (npc *NPC) getProtection() Protection_t { 23 | return 0 24 | } 25 | 26 | // TODO 27 | func (manager *NPCManager) addCreature(*NPC) { 28 | } 29 | 30 | // TODO 31 | func (m *NPCManager) heartbeat() { 32 | } 33 | 34 | func (npc *NPC) getHP(attr int) HP_t { 35 | return 1 36 | } 37 | -------------------------------------------------------------------------------- /gameserver/object.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | type ObjectClass int 8 | 9 | const ( 10 | OBJECT_CLASS_CREATURE ObjectClass = iota 11 | OBJECT_CLASS_ITEM 12 | OBJECT_CLASS_OBSTACLE 13 | OBJECT_CLASS_EFFECT 14 | OBJECT_CLASS_PORTAL 15 | ) 16 | 17 | // 每个游戏中的实体,都有一个ObjectID。客户端与服务端通过这个ID确定是什么对象 18 | type ObjectInterface interface { 19 | ObjectClass() ObjectClass 20 | ObjectInstance() *Object 21 | } 22 | 23 | // 派生类,都需要继承Object对象,并实现ObjectInterface接口 24 | type Object struct { 25 | ObjectID ObjectID_t 26 | Next ObjectInterface 27 | Prev ObjectInterface 28 | } 29 | 30 | func (obj *Object) ObjectInstance() *Object { 31 | return obj 32 | } 33 | -------------------------------------------------------------------------------- /gameserver/ouster.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/data" 5 | "github.com/tiancaiamao/ouster/log" 6 | . "github.com/tiancaiamao/ouster/util" 7 | "math/rand" 8 | "time" 9 | ) 10 | 11 | type WearPart uint 12 | 13 | const ( 14 | WEAR_CIRCLET = iota 15 | WEAR_COAT 16 | WEAR_LEFTHAND 17 | WEAR_RIGHTHAND 18 | WEAR_BOOTS 19 | WEAR_ARMSBAND1 20 | WEAR_ARMSBAND2 21 | WEAR_RING1 22 | WEAR_RING2 23 | WEAR_NECKLACE1 24 | WEAR_NECKLACE2 25 | WEAR_NECKLACE3 26 | WEAR_STONE1 27 | WEAR_STONE2 28 | WEAR_STONE3 29 | WEAR_STONE4 30 | WEAR_ZAP1 31 | WEAR_ZAP2 32 | WEAR_ZAP3 33 | WEAR_ZAP4 34 | WEAR_FASCIA 35 | WEAR_MITTEN 36 | OUSTERS_WEAR_MAX 37 | ) 38 | 39 | type Ouster struct { 40 | PlayerCreature //继承自PlayerCreature 41 | 42 | Competence byte 43 | CompetenceShape byte 44 | 45 | HairColor Color_t 46 | Alignment Alignment_t 47 | 48 | MP [3]MP_t 49 | 50 | Damage [3]Damage_t 51 | ToHit [2]ToHit_t 52 | Defense [2]Defense_t 53 | Protection [2]Protection_t 54 | AttackSpeed [2]Speed_t 55 | CriticalRatio [2]int 56 | 57 | GoalExp Exp_t 58 | Level Level_t 59 | 60 | Bonus Bonus_t 61 | SkillBonus SkillBonus_t 62 | 63 | Gold Gold_t 64 | Fame Fame_t 65 | 66 | VisionWidth ZoneCoord_t 67 | VisionHeight ZoneCoord_t 68 | 69 | // 技能糟 70 | SkillSlot map[SkillType_t]*OusterSkillSlot 71 | 72 | WearItem [OUSTERS_WEAR_MAX]*Item 73 | 74 | SilverDamage Silver_t 75 | 76 | HPStealAmount Steal_t 77 | HPStealRatio Steal_t 78 | 79 | MPStealAmount Steal_t 80 | MPStealRatio Steal_t 81 | 82 | HPRegen Regen_t 83 | MPRegen Regen_t 84 | 85 | Luck Luck_t 86 | 87 | ElementalFire Elemental_t 88 | ElementalWater Elemental_t 89 | ElementalEarth Elemental_t 90 | ElementalWind Elemental_t 91 | 92 | FireDamage Damage_t 93 | WaterDamage Damage_t 94 | EarthDamage Damage_t 95 | 96 | SilverResist Resist_t 97 | 98 | PassiveSkillMap map[SkillType_t]struct{} 99 | PassiveRatio int 100 | ExpSaveCount uint16 101 | FameSaveCount uint16 102 | AlignmentSaveCount uint16 103 | 104 | MPRegenTime time.Time 105 | 106 | EffectManager *EffectManager 107 | } 108 | 109 | func (ouster *Ouster) SkillInfo() data.PCSkillInfo { 110 | var ret data.OusterSkillInfo 111 | ret.LearnNewSkill = false 112 | skillList := make([]data.SubOusterSkillInfo, len(ouster.SkillSlot)) 113 | 114 | i := 0 115 | for _, slot := range ouster.SkillSlot { 116 | skillList[i].SkillType = slot.SkillType 117 | skillList[i].ExpLevel = slot.ExpLevel 118 | skillList[i].Interval = uint32(slot.Interval / time.Millisecond) 119 | skillList[i].CastingTime = uint32(slot.CastingTime / time.Millisecond) 120 | i++ 121 | } 122 | 123 | ret.SubOusterSkillInfoList = skillList 124 | return ret 125 | } 126 | 127 | func (ouster *Ouster) Init() { 128 | ouster.Creature.Init() 129 | ouster.EffectManager = NewEffectManager() 130 | } 131 | 132 | func (ouster *Ouster) CreatureClass() CreatureClass { 133 | return CREATURE_CLASS_OUSTER 134 | } 135 | 136 | func (ouster *Ouster) getProtection() Protection_t { 137 | return ouster.Protection[ATTR_CURRENT] 138 | } 139 | 140 | func (ouster *Ouster) PCInfo() data.PCInfo { 141 | info := &data.PCOusterInfo{ 142 | ObjectID: ouster.ObjectID, 143 | Name: ouster.Name, 144 | Level: ouster.Level, 145 | Sex: FEMALE, 146 | 147 | HairColor: ouster.HairColor, 148 | MasterEffectColor: ouster.MasterEffectColor, 149 | 150 | Alignment: ouster.Alignment, 151 | 152 | Rank: ouster.Rank, 153 | RankExp: ouster.RankExp, 154 | 155 | Exp: ouster.Exp, 156 | Fame: ouster.Fame, 157 | Gold: ouster.Gold, 158 | Sight: ouster.Sight, 159 | Bonus: ouster.Bonus, 160 | SilverDamage: ouster.SilverDamage, 161 | 162 | Competence: ouster.Competence, 163 | GuildID: ouster.GuildID, 164 | 165 | GuildMemberRank: ouster.GuildMemberRank, 166 | UnionID: ouster.UnionID, 167 | 168 | ZoneID: ouster.Scene.ZoneID, 169 | ZoneX: ZoneCoord_t(ouster.X), 170 | ZoneY: ZoneCoord_t(ouster.Y), 171 | } 172 | 173 | for _, v := range [...]int{ATTR_CURRENT, ATTR_MAX} { 174 | info.STR[v] = ouster.STR[v] 175 | info.DEX[v] = ouster.DEX[v] 176 | info.INI[v] = ouster.INI[v] 177 | info.HP[v] = ouster.HP[v] 178 | info.MP[v] = ouster.MP[v] 179 | } 180 | 181 | return info 182 | } 183 | 184 | func (ouster *Ouster) heartbeat() { 185 | ouster.EffectManager.heartbeat(time.Now()) 186 | } 187 | 188 | func (ouster *Ouster) computeDamage(creature CreatureInterface, bCritical bool) Damage_t { 189 | minDamage := ouster.Damage[ATTR_CURRENT] 190 | maxDamage := ouster.Damage[ATTR_MAX] 191 | // timeband := getZoneTimeband(pVampire->getZone()) 192 | // TODO 193 | timeband := 0 194 | // TODO 195 | // pItem := ouster.getWearItem(OUSTER_WEAR_RIGHTHAND) 196 | // 197 | // if pItem != nil { 198 | // MinDamage += pItem.getMinDamage() 199 | // MaxDamage += pItem.getMaxDamage() 200 | // } 201 | // 202 | 203 | scope := int(maxDamage - minDamage) 204 | if scope < 1 { 205 | scope = 1 206 | } 207 | realDamage := max(1, int(minDamage)+rand.Intn(scope)) 208 | 209 | var protection Protection_t 210 | if creature != nil { 211 | again: 212 | switch creature.(type) { 213 | case *Vampire: 214 | protection = creature.(*Vampire).Protection[ATTR_CURRENT] 215 | protection = Protection_t(getPercentValue(int(realDamage), VampireTimebandFactor[timeband])) 216 | case *Monster: 217 | protection = creature.(*Monster).Protection 218 | protection = Protection_t(getPercentValue(int(realDamage), VampireTimebandFactor[timeband])) 219 | case *Slayer: 220 | protection = creature.(*Slayer).Protection[ATTR_CURRENT] 221 | case *Ouster: 222 | protection = creature.(*Ouster).Protection[ATTR_CURRENT] 223 | case *Agent: 224 | creature = creature.(*Agent).PlayerCreatureInterface 225 | goto again 226 | default: 227 | log.Errorln("输入的参数不对") 228 | } 229 | } 230 | 231 | finalDamage := computeFinalDamage(minDamage, maxDamage, Damage_t(realDamage), protection, bCritical) 232 | 233 | return finalDamage 234 | } 235 | 236 | // TODO 237 | func (ouster *Ouster) hasSkill(skillType SkillType_t) *OusterSkillSlot { 238 | return nil 239 | } 240 | 241 | func (ouster *Ouster) getHP(attr int) HP_t { 242 | return ouster.HP[attr] 243 | } 244 | 245 | func getPercentValue(value, percent int) int { 246 | return value * percent / 100 247 | } 248 | -------------------------------------------------------------------------------- /gameserver/player.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/log" 5 | "github.com/tiancaiamao/ouster/packet" 6 | . "github.com/tiancaiamao/ouster/util" 7 | "io" 8 | "math/rand" 9 | "net" 10 | // "time" 11 | ) 12 | 13 | type Point struct { 14 | X int 15 | Y int 16 | } 17 | 18 | const ( 19 | ATTR_CURRENT = iota 20 | ATTR_MAX 21 | ATTR_BASE 22 | ) 23 | 24 | type PlayerStatus uint8 25 | 26 | const ( 27 | GPS_NONE = iota 28 | GPS_BEGIN_SESSION 29 | GPS_WAITING_FOR_CG_READY 30 | GPS_NORMAL 31 | GPS_IGNORE_ALL 32 | GPS_AFTER_SENDING_GL_INCOMING_CONNECTION 33 | GPS_END_SESSION 34 | ) 35 | 36 | // Player负责网络相关的处理,接收消息包,发送消息包 37 | type Player struct { 38 | PlayerStatus PlayerStatus 39 | ZoneID ZoneID_t 40 | OldZoneID ZoneID_t 41 | 42 | conn net.Conn 43 | packetReader *packet.Reader 44 | packetWriter *packet.Writer 45 | 46 | client <-chan packet.Packet 47 | send chan<- packet.Packet 48 | } 49 | 50 | func InitPlayer(player *Player, conn net.Conn) { 51 | player.PlayerStatus = GPS_BEGIN_SESSION 52 | player.conn = conn 53 | 54 | read := make(chan packet.Packet, 1) 55 | write := make(chan packet.Packet, 1) 56 | player.send = write 57 | player.client = read 58 | 59 | go func() { 60 | reader := packet.NewReader() 61 | player.packetReader = reader 62 | for { 63 | data, err := reader.Read(player.conn) 64 | if err != nil { 65 | if _, ok := err.(packet.NotImplementError); ok { 66 | log.Errorln("读到一个未实现的包:", data.PacketID()) 67 | } else { 68 | if err == io.EOF { 69 | log.Infoln("后台gouroutine读客户端失败了:", err) 70 | player.conn.Close() 71 | // 关闭读channel会使得agent的goroutine退出,回收资源 72 | close(read) 73 | return 74 | } else { 75 | log.Errorln("这是一个严重的错误:", err) 76 | return 77 | } 78 | } 79 | } 80 | log.Debugln("读到了一个packet:", data) 81 | read <- data 82 | } 83 | }() 84 | 85 | go func() { 86 | writer := packet.NewWriter() 87 | player.packetWriter = writer 88 | for { 89 | pkt, ok := <-write 90 | if !ok { 91 | // 关闭使读goroutine退出 92 | player.conn.Close() 93 | return 94 | } 95 | log.Debugf("write channel get a pkt: %#v\n", pkt) 96 | err := writer.Write(player.conn, pkt) 97 | if err != nil { 98 | log.Errorln(err) 99 | continue 100 | } 101 | } 102 | }() 103 | } 104 | 105 | func (player *Player) sendPacket(pkt packet.Packet) { 106 | player.send <- pkt 107 | } 108 | 109 | // if tohit == dodge, the default formula is 0.85 110 | // if tohit < dodge, then tohit / dodge should be primary factor, also take other factor into consideration 111 | // if tohit > dodge, then the differential should be important, also dodge. 112 | func HitTest(tohit uint16, dodge uint16) bool { 113 | var prob float32 114 | if tohit < dodge { 115 | prob = 0.85*float32(tohit)/float32(dodge) - 0.15*float32(dodge-tohit)/float32(tohit) 116 | } else { 117 | prob = 0.85 + 0.15*float32(tohit-dodge)/float32(dodge) 118 | } 119 | 120 | return rand.Float32() < prob 121 | } 122 | 123 | // func (player *Player) Save() { 124 | // f, err := os.Create(os.Getenv("HOME") + "/.ouster/player/" + player.Name) 125 | // if err != nil { 126 | // return 127 | // } 128 | // encoder := json.NewEncoder(f) 129 | // 130 | // // pcInfo := player.PCInfo() 131 | // skillInfo := player.SkillInfo() 132 | // 133 | // // encoder.Encode(pcInfo) 134 | // encoder.Encode(skillInfo) 135 | // 136 | // f.Close() 137 | // } 138 | 139 | func Encrypt(ZoneID uint16, ServerID uint16) uint8 { 140 | return uint8(((ZoneID >> 8) ^ ZoneID) ^ ((ServerID + 1) << 4)) 141 | } 142 | 143 | func (player *Player) BroadcastPacket(x uint8, y uint8, pkt packet.Packet) { 144 | // player.Scene.Nearby(x, y, func(watcher aoi.Entity, marker aoi.Entity) { 145 | // id := marker.Id() 146 | // if id != player.Id() { 147 | // object := player.Scene.objects[id] 148 | // if nearby, ok := object.(*Player); ok { 149 | // nearby.send <- pkt 150 | // } 151 | // } 152 | // }) 153 | } 154 | 155 | type BaseAttack struct{} 156 | 157 | type SkillEffect struct { 158 | Id int 159 | To uint32 160 | Succ bool 161 | Hurt int 162 | } 163 | -------------------------------------------------------------------------------- /gameserver/playercreature.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "github.com/tiancaiamao/ouster/config" 7 | "github.com/tiancaiamao/ouster/data" 8 | "github.com/tiancaiamao/ouster/log" 9 | "github.com/tiancaiamao/ouster/packet" 10 | . "github.com/tiancaiamao/ouster/util" 11 | "os" 12 | "time" 13 | ) 14 | 15 | type PlayerCreatureInterface interface { 16 | CreatureInterface 17 | PlayerCreatureInstance() *PlayerCreature 18 | 19 | PCInfo() data.PCInfo 20 | SkillInfo() data.PCSkillInfo 21 | computeDamage(CreatureInterface, bool) Damage_t 22 | heartbeat() 23 | } 24 | 25 | type PlayerCreature struct { 26 | Creature 27 | 28 | Name string 29 | // 装备 30 | // 物品 31 | // 钱 32 | GuildID GuildID_t 33 | // 阶级 34 | Rank Rank_t 35 | RankExp RankExp_t 36 | 37 | Exp Exp_t 38 | 39 | GuildMemberRank GuildMemberRank_t 40 | UnionID uint32 41 | AdvancementLevel Level_t 42 | AdvancementGoalExp Exp_t 43 | // 任务管理 44 | 45 | MagicBonusDamage Damage_t 46 | PhysicBonusDamage Damage_t 47 | 48 | MagicDamageReduce Damage_t 49 | PhysicDamageReduce Damage_t 50 | 51 | // 宠物信息 52 | // 昵称 53 | // 圣书 54 | BaseLuck Luck_t 55 | // 商店 56 | 57 | PowerPoint int 58 | IsAdvanced bool 59 | // 转职 60 | AdvancedSTR Attr_t 61 | AdvancedDEX Attr_t 62 | AdvancedINT Attr_t 63 | AdvancedAttrBonus Attr_t 64 | 65 | MasterEffectColor uint8 66 | 67 | STR [3]Attr_t 68 | DEX [3]Attr_t 69 | INI [3]Attr_t 70 | 71 | HP [3]HP_t 72 | } 73 | 74 | func (pc *PlayerCreature) PlayerCreatureInstance() *PlayerCreature { 75 | return pc 76 | } 77 | 78 | func LoadPlayerCreature(name string, typ packet.PCType) (ptr PlayerCreatureInterface, zoneID ZoneID_t, err error) { 79 | fileName := config.DataDir + name 80 | log.Debugln("load文件", fileName) 81 | f, err := os.Open(fileName) 82 | if err != nil { 83 | return 84 | } 85 | defer f.Close() 86 | 87 | decoder := json.NewDecoder(f) 88 | 89 | switch typ { 90 | case packet.PC_VAMPIRE: 91 | return loadVampire(decoder) 92 | case packet.PC_OUSTER: 93 | return loadOuster(decoder) 94 | case packet.PC_SLAYER: 95 | return loadSlayer(decoder) 96 | } 97 | return nil, 0, errors.New("player type error!") 98 | } 99 | 100 | func loadOuster(decoder *json.Decoder) (ouster *Ouster, zoneID ZoneID_t, err error) { 101 | var pcInfo data.PCOusterInfo 102 | err = decoder.Decode(&pcInfo) 103 | if err != nil { 104 | log.Errorln("decode pcinfo failed") 105 | return 106 | } 107 | 108 | ouster = new(Ouster) 109 | ouster.Init() 110 | ouster.Name = pcInfo.Name 111 | ouster.Level = pcInfo.Level 112 | ouster.HairColor = pcInfo.HairColor 113 | ouster.MasterEffectColor = pcInfo.MasterEffectColor 114 | ouster.Alignment = pcInfo.Alignment 115 | ouster.STR = pcInfo.STR 116 | ouster.DEX = pcInfo.DEX 117 | ouster.INI = pcInfo.INI 118 | ouster.HP[ATTR_MAX] = pcInfo.HP[ATTR_MAX] 119 | ouster.HP[ATTR_CURRENT] = pcInfo.HP[ATTR_CURRENT] 120 | ouster.MP[ATTR_MAX] = pcInfo.MP[ATTR_MAX] 121 | ouster.MP[ATTR_CURRENT] = pcInfo.MP[ATTR_CURRENT] 122 | ouster.Rank = pcInfo.Rank 123 | ouster.RankExp = pcInfo.RankExp 124 | ouster.Exp = pcInfo.Exp 125 | ouster.Fame = pcInfo.Fame 126 | ouster.Sight = pcInfo.Sight 127 | ouster.Bonus = pcInfo.Bonus 128 | ouster.Competence = pcInfo.Competence 129 | ouster.GuildMemberRank = pcInfo.GuildMemberRank 130 | ouster.AdvancementLevel = pcInfo.AdvancementLevel 131 | 132 | zoneID = pcInfo.ZoneID 133 | ouster.X = pcInfo.ZoneX 134 | ouster.Y = pcInfo.ZoneY 135 | 136 | var skillInfo data.OusterSkillInfo 137 | err = decoder.Decode(&skillInfo) 138 | if err != nil { 139 | return 140 | } 141 | 142 | ouster.SkillSlot = make(map[SkillType_t]*OusterSkillSlot) 143 | for _, v := range skillInfo.SubOusterSkillInfoList { 144 | skillslot := &OusterSkillSlot{ 145 | // Name string 146 | SkillType: v.SkillType, 147 | ExpLevel: v.ExpLevel, 148 | Interval: time.Duration(v.Interval) * time.Millisecond, 149 | CastingTime: time.Duration(v.CastingTime) * time.Millisecond, 150 | // RunTime time.Time 151 | } 152 | ouster.SkillSlot[v.SkillType] = skillslot 153 | } 154 | 155 | return 156 | } 157 | 158 | func loadSlayer(decoder *json.Decoder) (slayer *Slayer, zoneID ZoneID_t, err error) { 159 | var pcInfo data.PCSlayerInfo 160 | err = decoder.Decode(&pcInfo) 161 | if err != nil { 162 | log.Errorln("decode pcinfo failed") 163 | return 164 | } 165 | 166 | slayer = new(Slayer) 167 | slayer.Init() 168 | slayer.Name = pcInfo.Name 169 | slayer.HairColor = pcInfo.HairColor 170 | slayer.MasterEffectColor = pcInfo.MasterEffectColor 171 | slayer.Alignment = pcInfo.Alignment 172 | slayer.STR = pcInfo.STR 173 | slayer.DEX = pcInfo.DEX 174 | slayer.INI = pcInfo.INI 175 | slayer.HP[ATTR_MAX] = pcInfo.HP[ATTR_MAX] 176 | slayer.HP[ATTR_CURRENT] = pcInfo.HP[ATTR_CURRENT] 177 | slayer.MP[ATTR_MAX] = pcInfo.MP[ATTR_MAX] 178 | slayer.MP[ATTR_CURRENT] = pcInfo.MP[ATTR_CURRENT] 179 | slayer.Rank = pcInfo.Rank 180 | slayer.RankExp = pcInfo.RankExp 181 | slayer.Fame = pcInfo.Fame 182 | slayer.Sight = pcInfo.Sight 183 | slayer.Competence = pcInfo.Competence 184 | slayer.GuildMemberRank = pcInfo.GuildMemberRank 185 | slayer.AdvancementLevel = pcInfo.AdvancementLevel 186 | 187 | for i := 0; i < 6; i++ { 188 | slayer.DomainLevels[i] = pcInfo.DomainLevels[i] 189 | slayer.DomainExps[i] = pcInfo.DomainExps[i] 190 | } 191 | 192 | zoneID = pcInfo.ZoneID 193 | slayer.X = ZoneCoord_t(pcInfo.ZoneX) 194 | slayer.Y = ZoneCoord_t(pcInfo.ZoneY) 195 | 196 | // var skillInfo packet.OusterSkillInfo 197 | // err = decoder.Decode(&skillInfo) 198 | // if err != nil { 199 | // return 200 | // } 201 | // 202 | // slayer.SkillSlot = make(map[SkillType_t]*OusterSkillSlot) 203 | // for _, v := range skillInfo.SubOusterSkillInfoList { 204 | // skillslot := &OusterSkillSlot{ 205 | // // Name string 206 | // SkillType: v.SkillType, 207 | // ExpLevel: v.ExpLevel, 208 | // Interval: time.Duration(v.Interval) * time.Millisecond, 209 | // CastingTime: time.Duration(v.CastingTime) * time.Millisecond, 210 | // // RunTime time.Time 211 | // } 212 | // slayer.SkillSlot[v.SkillType] = skillslot 213 | // } 214 | 215 | return 216 | } 217 | 218 | func loadVampire(decoder *json.Decoder) (vampire *Vampire, zoneID ZoneID_t, err error) { 219 | var pcInfo data.PCVampireInfo 220 | err = decoder.Decode(&pcInfo) 221 | if err != nil { 222 | return 223 | } 224 | 225 | vampire = new(Vampire) 226 | vampire.Init() 227 | vampire.Name = pcInfo.Name 228 | vampire.Level = pcInfo.Level 229 | vampire.Sex = pcInfo.Sex 230 | vampire.SkinColor = pcInfo.SkinColor 231 | vampire.Alignment = pcInfo.Alignment 232 | vampire.STR = pcInfo.STR 233 | vampire.DEX = pcInfo.DEX 234 | vampire.INI = pcInfo.INI 235 | vampire.HP[ATTR_MAX] = pcInfo.HP[ATTR_MAX] 236 | vampire.HP[ATTR_CURRENT] = pcInfo.HP[ATTR_CURRENT] 237 | vampire.Rank = pcInfo.Rank 238 | vampire.RankExp = pcInfo.RankExp 239 | vampire.Exp = pcInfo.Exp 240 | vampire.Fame = pcInfo.Fame 241 | vampire.Sight = pcInfo.Sight 242 | vampire.Bonus = pcInfo.Bonus 243 | vampire.Competence = pcInfo.Competence 244 | vampire.GuildMemberRank = pcInfo.GuildMemberRank 245 | vampire.AdvancementLevel = pcInfo.AdvancementLevel 246 | 247 | zoneID = pcInfo.ZoneID 248 | vampire.X = ZoneCoord_t(pcInfo.ZoneX) 249 | vampire.Y = ZoneCoord_t(pcInfo.ZoneY) 250 | 251 | if vampire.X == 0 || vampire.Y == 0 { 252 | log.Debugln("不科学呀", vampire.X, vampire.Y) 253 | } 254 | return 255 | } 256 | -------------------------------------------------------------------------------- /gameserver/portal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type PortalType_t byte 4 | 5 | const ( 6 | PORTAL_CLASS_NORMAL = iota 7 | PORTAL_CLASS_PRIVATE 8 | PORTAL_CLASS_MULTI 9 | PORTAL_CLASS_TRIGGERED 10 | ) 11 | 12 | type PortalType byte 13 | 14 | const ( 15 | PORTAL_NORMAL PortalType = iota 16 | PORTAL_SLAYER 17 | PORTAL_VAMPIRE 18 | PORTAL_MULTI_TARGET 19 | PORTAL_PRIVATE 20 | PORTAL_GUILD 21 | PORTAL_BATTLE 22 | PORTAL_OUSTER 23 | ) 24 | -------------------------------------------------------------------------------- /gameserver/skill.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | var ( 8 | VampireTimebandFactor = [4]int{125, 100, 125, 150} 9 | MonsterTimebandFactor = [4]int{75, 50, 75, 100} 10 | AttrExpTimebandFactor = [4]int{100, 100, 100, 150} 11 | DomainExpTimebandFactor = [4]int{100, 100, 100, 150} 12 | ) 13 | 14 | type SkillPropertyType uint8 15 | 16 | const ( 17 | SKILL_PROPERTY_TYPE_MELEE SkillPropertyType = iota 18 | SKILL_PROPERTY_TYPE_MAGIC 19 | SKILL_PROPERTY_TYPE_PHYSIC 20 | ) 21 | 22 | const ( 23 | SKILL_ATTACK_MELEE = 0 24 | SKILL_BLOOD_DRAIN = 79 25 | SKILL_UN_BURROW = 107 26 | SKILL_UN_TRANSFORM = 108 27 | SKILL_UN_INVISIBILITY = 109 28 | SKILL_RAPID_GLIDING = 203 29 | SKILL_METEOR_STRIKE = 180 30 | SKILL_INVISIBILITY = 100 31 | SKILL_PARALYZE = 89 32 | SKILL_BLOOD_SPEAR = 97 33 | SKILL_ABSORB_SOUL = 246 34 | SKILL_SUMMON_SYLPH = 247 35 | SKILL_SHARP_HAIL = 348 // 尖锐冰雹 36 | SKILL_FLOURISH = 219 // 活跃攻击 37 | SKILL_DESTRUCTION_SPEAR = 298 //致命爆发 38 | SKILL_SHARP_CHAKRAM = 295 // 税利之轮 39 | SKILL_EVADE = 220 // 回避术 40 | SKILL_FIRE_OF_SOUL_STONE = 227 41 | SKILL_ICE_OF_SOUL_STONE = 228 42 | SKILL_SAND_OF_SOUL_STONE = 229 43 | SKILL_TELEPORT = 280 // 瞬间移动 44 | SKILL_DUCKING_WALLOP = 302 // 光速冲击 45 | SKILL_DISTANCE_BLITZ = 304 // 雷神斩 46 | ) 47 | 48 | type SkillFormula interface { 49 | ComputeOutput(*Creature, *Creature) SkillOutput 50 | } 51 | 52 | type PlayerSkillToTileHandler interface { 53 | ExecuteP2T(*Player, uint8, uint8) 54 | } 55 | 56 | type MonsterSkillToTileHandler interface { 57 | ExecuteM2T(*Monster, uint8, uint8) 58 | } 59 | 60 | type PlayerSkillToMonsterHandler interface { 61 | ExecuteP2M(*Player, *Monster) 62 | } 63 | 64 | type MonsterSkillToPlayerHandler interface { 65 | ExecuteM2P(*Monster, *Player) 66 | } 67 | 68 | type PlayerSkillToPlayerHandler interface { 69 | ExecuteP2P(*Player, *Player) 70 | } 71 | 72 | type SkillToSelfHandler interface { 73 | Execute(*Player) 74 | } 75 | 76 | type Skill struct { 77 | } 78 | 79 | // 派生类中重写这个函数 80 | func (skill Skill) ComputeOutput(*SkillInput, *SkillOutput) {} 81 | func (skill Skill) Check(skillType SkillType_t, agent *Agent) bool { 82 | // skillSlot := agent.hasSkill(skillType) 83 | // skillInfo := skillInfoTable[skillType] 84 | requireMP := decreaseConsumeMP(agent) 85 | 86 | // var hitBonus int 87 | // if agent.hasRankBonus(RANK_BONUS_KNOWLEDGE_OF_INNATE) { 88 | // rankBonus := agent.getRankBonus(RANK_BONUS_KNOWLEDGE_OF_INNATE) 89 | // hitBonus = rankBonus.getPoint() 90 | // } 91 | 92 | manaCheck := hasEnoughMana(agent, requireMP) 93 | if !manaCheck { 94 | return false 95 | } 96 | 97 | // timeCheck := verifyRuntime(skillSlot) 98 | // if !timcCheck { 99 | // return false 100 | // } 101 | 102 | // hitRoll := HitRoll.isSuccessMagic(ouster, skillInfo, skillSlot, hitBonus) 103 | // if !hitRoll { 104 | // return false 105 | // } 106 | 107 | pc := agent.PlayerCreatureInstance() 108 | effected := pc.IsFlag(EFFECT_CLASS_INVISIBILITY) || pc.IsFlag(EFFECT_CLASS_HAS_FLAG) || pc.IsFlag(EFFECT_CLASS_HAS_SWEEPER) 109 | if effected { 110 | return false 111 | } 112 | 113 | decreaseMana(agent, requireMP) 114 | // skillSlot.setRunTime(output.Delay) 115 | return true 116 | } 117 | 118 | var skillTable map[SkillType_t]SkillHandler 119 | 120 | func init() { 121 | skillTable = make(map[SkillType_t]SkillHandler) 122 | 123 | skillTable[SKILL_ATTACK_MELEE] = AttackMelee{} 124 | skillTable[SKILL_BLOOD_DRAIN] = BloodDrain{} 125 | skillTable[SKILL_SHARP_HAIL] = SharpHail{} 126 | skillTable[SKILL_DESTRUCTION_SPEAR] = DestructionSpear{} 127 | skillTable[SKILL_FLOURISH] = Flourish{} 128 | skillTable[SKILL_TELEPORT] = Teleport{} 129 | skillTable[SKILL_DUCKING_WALLOP] = DuckingWallop{} 130 | skillTable[SKILL_EVADE] = Evade{} 131 | skillTable[SKILL_SHARP_CHAKRAM] = SharpChakram{} 132 | 133 | // &SkillInfo{ 134 | // Type: SKILL_PROPERTY_TYPE_PHYSIC, 135 | // Name: "Sharp Hail", 136 | // ConsumeMP: 20, 137 | // } 138 | // skillTable[SKILL_BLOOD_SPEAR] = &SkillInfo{ 139 | // Type: SKILL_PROPERTY_TYPE_MAGIC, 140 | // Name: "Bloody Spear", 141 | // ConsumeMP: 60, 142 | // } 143 | // skillTable[SKILL_PARALYZE] = &SkillInfo{ 144 | // Type: SKILL_PROPERTY_TYPE_MAGIC, 145 | // Name: "Paralyze", 146 | // ConsumeMP: 30, 147 | // } 148 | // skillTable[SKILL_RAPID_GLIDING] = &SkillInfo{ 149 | // Type: SKILL_PROPERTY_TYPE_MAGIC, 150 | // Name: "Rapid Gliding", 151 | // ConsumeMP: 23, 152 | // } 153 | // skillTable[SKILL_INVISIBILITY] = &SkillInfo{ 154 | // Type: SKILL_PROPERTY_TYPE_MAGIC, 155 | // Name: "Invisibility", 156 | // ConsumeMP: 36, 157 | // } 158 | // skillTable[SKILL_METEOR_STRIKE] = &SkillInfo{ 159 | // Type: SKILL_PROPERTY_TYPE_MAGIC, 160 | // Name: "Meteor Strike", 161 | // ConsumeMP: 53, 162 | // } 163 | // 164 | } 165 | 166 | type Invisibility struct { 167 | Skill 168 | } 169 | 170 | type MeteorStrike struct { 171 | Skill 172 | } 173 | type Paralyze struct { 174 | Skill 175 | } 176 | type AttackMelee struct { 177 | Skill 178 | } 179 | type BloodDrain struct { 180 | Skill 181 | } 182 | type SharpHail struct { 183 | Skill 184 | } 185 | 186 | type DestructionSpear struct { 187 | Skill 188 | } 189 | 190 | type Flourish struct { 191 | Skill 192 | } 193 | 194 | type Teleport struct { 195 | Skill 196 | } 197 | 198 | type DuckingWallop struct { 199 | Skill 200 | 201 | DuckingWallopMask [8][18]POINT 202 | DamageRatio [18]int 203 | } 204 | 205 | type Evade struct { 206 | Skill 207 | } 208 | type SharpChakram struct { 209 | Skill 210 | } 211 | 212 | func Distance2(x1, y1, x2, y2 uint8) int { 213 | d1 := int(x1) - int(x2) 214 | d2 := int(y1) - int(y2) 215 | return d1*d1 + d2*d2 216 | } 217 | 218 | func inRange(radius uint8, x uint8, y uint8, middleX uint8, middleY uint8) bool { 219 | return x >= middleX-radius/2 && 220 | x <= middleX+radius/2 && 221 | y >= middleY-radius/2 && 222 | y <= middleY-radius/2 223 | } 224 | -------------------------------------------------------------------------------- /gameserver/skill2object.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | // "github.com/tiancaiamao/ouster/packet" 5 | ) 6 | 7 | type BloodSpearHandler struct{} 8 | 9 | func (blood BloodSpearHandler) ExecuteP2M(player *Player, monster *Monster) { 10 | // player.send <- &packet.GCSkillToObjectOK1{ 11 | // SkillType: SKILL_BLOOD_SPEAR, 12 | // CEffectID: 0, 13 | // TargetObjectID: uint32(monster.ObjectID), 14 | // } 15 | // player.BroadcastPacket(player.X(), player.Y(), &packet.GCSkillToObjectOK3{ 16 | // ObjectID: player.Id(), 17 | // SkillType: SKILL_BLOOD_SPEAR, 18 | // TargetX: monster.X(), 19 | // TargetY: monster.Y(), 20 | // }) 21 | // player.BroadcastPacket(monster.X(), monster.Y(), &packet.GCSkillToObjectOK4{ 22 | // ObjectID: player.Id(), 23 | // SkillType: SKILL_BLOOD_SPEAR, 24 | // }) 25 | 26 | // damage := player.STR[ATTR_CURRENT]/6 + player.INT[ATTR_CURRENT]/2 + player.DEX[ATTR_CURRENT]/12 27 | // if damage >= 180 { 28 | // damage = 180 29 | // } 30 | // player.Scene.agent <- AgentMessage{ 31 | // Player: player, 32 | // Msg: SkillOutput{ 33 | // MonsterID: monster.Id(), 34 | // // Damage: int(damage), 35 | // Duration: 10, 36 | // }, 37 | // } 38 | } 39 | func (blood BloodSpearHandler) ExecuteM2P(monster *Monster, player *Player) { 40 | // TODO 41 | } 42 | func (blood BloodSpearHandler) ExecuteP2P(p1 *Player, p2 *Player) { 43 | // TODO 44 | } 45 | 46 | type ParalyzeHandler struct{} 47 | 48 | func (ignore ParalyzeHandler) ExecuteP2M(player *Player, monster *Monster) { 49 | // skillOutput := ignore.ComputeOutput(&player.Creature, &monster.Creature) 50 | // ok := &packet.GCSkillToObjectOK1{ 51 | // SkillType: SKILL_PARALYZE, 52 | // TargetObjectID: monster.Id(), 53 | // Duration: uint16(skillOutput.Duration), 54 | // } 55 | // player.send <- ok 56 | // player.Scene.BroadcastPacket(player.X(), player.Y(), &packet.GCSkillToObjectOK3{ 57 | // ObjectID: player.Id(), 58 | // SkillType: SKILL_PARALYZE, 59 | // TargetX: monster.X(), 60 | // TargetY: monster.Y(), 61 | // }) 62 | // player.Scene.BroadcastPacket(monster.X(), monster.Y(), &packet.GCSkillToObjectOK4{ 63 | // ObjectID: monster.Id(), 64 | // SkillType: SKILL_PARALYZE, 65 | // Duration: uint16(skillOutput.Duration), 66 | // }) 67 | } 68 | func (ignore ParalyzeHandler) ExecuteM2P(monster *Monster, player *Player) { 69 | // TODO 70 | } 71 | func (ignore ParalyzeHandler) ExecuteP2P(p1 *Player, p2 *Player) { 72 | // TODO 73 | } 74 | -------------------------------------------------------------------------------- /gameserver/skill2self.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type InvisibilityHandler struct{} 4 | 5 | func (ignore InvisibilityHandler) Execute(player *Player) { 6 | // TODO 7 | } 8 | -------------------------------------------------------------------------------- /gameserver/skill2tile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | // "github.com/tiancaiamao/ouster/packet" 5 | ) 6 | 7 | type SharpHailHandler struct{} 8 | 9 | func (ignore SharpHailHandler) ExecuteP2T(player *Player, x uint8, y uint8) { 10 | // slot := player.SkillSlot(SKILL_SHARP_HAIL) 11 | 12 | // ok := &packet.GCSkillToTileOK1{ 13 | // SkillType: SKILL_SHARP_HAIL, 14 | // Duration: 10, 15 | // Range: 5, 16 | // X: x, 17 | // Y: y, 18 | // } 19 | // player.send <- ok 20 | 21 | // AOE(player.Scene, player, x, y, skillTable[SKILL_SHARP_HAIL], slot) 22 | } 23 | 24 | type RapidGlidingHandler struct{} 25 | 26 | func (ignore RapidGlidingHandler) ExecuteP2T(player *Player, x uint8, y uint8) { 27 | // fastMove := &packet.GCFastMovePacket{ 28 | // ObjectID: player.Id(), 29 | // FromX: player.X(), 30 | // FromY: player.Y(), 31 | // ToX: x, 32 | // ToY: y, 33 | // SkillType: SKILL_RAPID_GLIDING, 34 | // } 35 | // player.Scene.agent <- AgentMessage{ 36 | // Player: player, 37 | // Msg: fastMove, 38 | // } 39 | 40 | // ok := &packet.GCSkillToTileOK1{ 41 | // SkillType: uint16(SKILL_RAPID_GLIDING), 42 | // Duration: 10, 43 | // Range: 1, 44 | // X: x, 45 | // Y: y, 46 | // } 47 | // player.send <- ok 48 | } 49 | 50 | type MeteorStrikeHandler struct{} 51 | 52 | func (ignore MeteorStrikeHandler) ExecuteP2T(player *Player, x uint8, y uint8) { 53 | // player.Scene.Nearby(x, y, func(watcher aoi.Entity, marker aoi.Entity) { 54 | // if x >= marker.X()-1 && 55 | // x <= marker.X()+1 && 56 | // y >= marker.Y()-1 && 57 | // y <= marker.Y()+1 { 58 | // id := marker.Id() 59 | // obj := player.Scene.objects[id] 60 | // switch obj.(type) { 61 | // case *Monster: 62 | // monster := obj.(*Monster) 63 | // skillOutput := ignore.ComputeOutput(&player.Creature, &monster.Creature) 64 | // player.Scene.agent <- AgentMessage{ 65 | // Player: player, 66 | // Msg: skillOutput, 67 | // } 68 | // case *Player: 69 | // 70 | // } 71 | // } 72 | // }) 73 | // ok := &packet.GCSkillToTileOK1{ 74 | // SkillType: SKILL_METEOR_STRIKE, 75 | // Duration: 10, 76 | // Range: 3, 77 | // X: x, 78 | // Y: y, 79 | // } 80 | // player.send <- ok 81 | } 82 | -------------------------------------------------------------------------------- /gameserver/skillformula.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/log" 5 | . "github.com/tiancaiamao/ouster/util" 6 | "math/rand" 7 | ) 8 | 9 | func computeFinalDamage( 10 | minDamage Damage_t, 11 | maxDamage Damage_t, 12 | realDamage Damage_t, 13 | protection Protection_t, 14 | bCritical bool) Damage_t { 15 | // 致命一击无视防御 16 | if bCritical { 17 | return realDamage 18 | } 19 | 20 | finalDamage := realDamage - (realDamage*(Damage_t(protection)/8))/100 21 | 22 | return finalDamage 23 | 24 | // avgDamage := (minDamage + maxDamage) / 2 25 | // 26 | // int DamageRatio = 100; 27 | // 28 | // if (Protection < avgDamage) 29 | // { 30 | // DamageRatio = 100; 31 | // } 32 | // else if (Protection < getPercentValue(avgDamage, 150)) 33 | // { 34 | // DamageRatio = 90; 35 | // } 36 | // else if (Protection < getPercentValue(avgDamage, 200)) 37 | // { 38 | // DamageRatio = 80; 39 | // } 40 | // else if (Protection < getPercentValue(avgDamage, 250)) 41 | // { 42 | // DamageRatio = 70; 43 | // } 44 | // else if (Protection < getPercentValue(avgDamage, 300)) 45 | // { 46 | // DamageRatio = 60; 47 | // } 48 | // else 49 | // { 50 | // DamageRatio = 50; 51 | // } 52 | 53 | // return max(1, getPercentValue(realDamage, DamageRatio)) 54 | } 55 | 56 | func (ignore MeteorStrike) ComputeOutput(c1 *SkillInput, c2 *SkillOutput) { 57 | return 58 | // Damage: int(float32(c1.Level)*0.8) + 59 | // int(c1.STR[ATTR_CURRENT]+c1.DEX[ATTR_CURRENT])/6, 60 | } 61 | 62 | func (ignore Paralyze) ComputeOutput(c1 *SkillInput, c2 *SkillOutput) { 63 | return 64 | // Duration: int((3 + c1.INT[ATTR_CURRENT]/15) * 10), 65 | 66 | } 67 | 68 | func HitRoll(pAttacker CreatureInterface, pDefender CreatureInterface, bonus int) bool { 69 | if pDefender.CreatureInstance().isFlag(EFFECT_CLASS_NO_DAMAGE) { 70 | return false 71 | } 72 | 73 | var ( 74 | tohit ToHit_t 75 | defense Defense_t 76 | ) 77 | //TODO 78 | // timeband = pZone->getTimeband(); 79 | timeband := 0 80 | 81 | again1: 82 | switch pAttacker.(type) { 83 | case *Slayer: 84 | tohit = pAttacker.(*Slayer).ToHit[ATTR_CURRENT] 85 | case *Ouster: 86 | tohit = pAttacker.(*Ouster).ToHit[ATTR_CURRENT] 87 | case *Vampire: 88 | tohit = pAttacker.(*Vampire).ToHit[ATTR_CURRENT] 89 | tohit = ToHit_t(getPercentValue(int(tohit), VampireTimebandFactor[timeband])) 90 | case *Monster: 91 | tohit = pAttacker.(*Monster).ToHit 92 | tohit = ToHit_t(getPercentValue(int(tohit), VampireTimebandFactor[timeband])) 93 | case *Agent: 94 | pAttacker = pAttacker.(*Agent).PlayerCreatureInterface 95 | goto again1 96 | default: 97 | log.Errorln("参数不对") 98 | } 99 | 100 | again2: 101 | switch pDefender.(type) { 102 | case *Slayer: 103 | defense = pDefender.(*Slayer).Defense[ATTR_CURRENT] 104 | case *Ouster: 105 | defense = pDefender.(*Ouster).Defense[ATTR_CURRENT] 106 | case *Vampire: 107 | defense = pDefender.(*Vampire).Defense[ATTR_CURRENT] 108 | defense = Defense_t(getPercentValue(int(defense), VampireTimebandFactor[timeband])) 109 | case *Monster: 110 | defense = pDefender.(*Monster).Defense 111 | defense = Defense_t(getPercentValue(int(defense), VampireTimebandFactor[timeband])) 112 | case *Agent: 113 | pDefender = pDefender.(*Agent).PlayerCreatureInterface 114 | goto again2 115 | } 116 | 117 | randValue := rand.Intn(100) 118 | var result int 119 | 120 | if int(tohit) >= int(defense) { 121 | result = min(90, int(int((float64(int(tohit)-int(defense))/1.5))+60)+bonus) 122 | } else { 123 | if _, ok := pAttacker.(*Monster); ok { 124 | result = max(10, (int)(60-int((float64(defense)-float64(tohit))/1.5)+bonus)) 125 | } else { 126 | result = max(20, (int)(60-int((float64(defense)-float64(tohit))/1.5)+bonus)) 127 | } 128 | } 129 | 130 | if randValue <= result { 131 | return true 132 | } 133 | 134 | return false 135 | } 136 | -------------------------------------------------------------------------------- /gameserver/skillinfo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | var ( 8 | skillInfoTable map[SkillType_t]SkillInfo 9 | ) 10 | 11 | type SkillInfo struct { 12 | Type uint 13 | Name string 14 | MinDamage uint 15 | MaxDamage uint 16 | MinDelay uint 17 | MaxDelay uint 18 | MinCastTime uint 19 | MaxCastTime uint 20 | 21 | MinDuration int 22 | MaxDuration int 23 | 24 | ConsumeMP uint 25 | 26 | MaxRange uint 27 | MinRange uint 28 | 29 | // 0x01 : burrowing 30 | // 0x02 : walking 31 | // 0x04 : flying 32 | Target uint 33 | 34 | SubSkill uint 35 | Point uint 36 | Domain byte 37 | 38 | MagicDomain int 39 | ElementDomain int 40 | 41 | SkillPoint int 42 | LevelUpPoint int 43 | 44 | RequireSkills []SkillType_t 45 | RequiredSkills []SkillType_t 46 | 47 | CanDelete byte 48 | 49 | RequireFire Elemental_t 50 | RequireWater Elemental_t 51 | RequireEarth Elemental_t 52 | RequireWind Elemental_t 53 | RequireSum Elemental_t 54 | 55 | RequireWristletElemental ElementalType 56 | RequireStone1Elemental ElementalType 57 | RequireStone2Elemental ElementalType 58 | RequireStone3Elemental ElementalType 59 | RequireStone4Elemental ElementalType 60 | } 61 | -------------------------------------------------------------------------------- /gameserver/skillslot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | "time" 6 | ) 7 | 8 | type SlayerSkillSlot struct { 9 | Name string 10 | SkillType SkillType_t 11 | Exp Exp_t 12 | ExpLevel ExpLevel_t 13 | Interval time.Duration 14 | CastingTime time.Duration 15 | RunTime time.Time 16 | Enable bool 17 | } 18 | 19 | type VampireSkillSlot struct { 20 | Name string 21 | SkillType SkillType_t 22 | Interval time.Duration 23 | CastingTime time.Duration 24 | RunTime time.Time 25 | } 26 | 27 | type OusterSkillSlot struct { 28 | Name string 29 | SkillType SkillType_t 30 | ExpLevel ExpLevel_t 31 | Interval time.Duration 32 | CastingTime time.Duration 33 | RunTime time.Time 34 | } 35 | 36 | type SkillSlot struct { 37 | SkillType uint16 38 | ExpLevel uint16 39 | 40 | LastUse time.Time 41 | Cooling uint16 42 | Duration uint16 43 | 44 | Interval uint32 45 | CastingTime uint32 46 | } 47 | 48 | func verifyRunTime(skillSlot SkillSlot) bool { 49 | return true 50 | } 51 | -------------------------------------------------------------------------------- /gameserver/slayer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/data" 5 | "github.com/tiancaiamao/ouster/packet" 6 | . "github.com/tiancaiamao/ouster/util" 7 | "time" 8 | ) 9 | 10 | const ( 11 | SLAYER_WEAR_HEAD = iota 12 | SLAYER_WEAR_NECK 13 | SLAYER_WEAR_BODY 14 | SLAYER_WEAR_LEFTHAND 15 | SLAYER_WEAR_RIGHTHAND 16 | SLAYER_WEAR_HAND3 17 | SLAYER_WEAR_BELT 18 | SLAYER_WEAR_LEG 19 | SLAYER_WEAR_WRIST1 20 | SLAYER_WEAR_WRIST2 21 | SLAYER_WEAR_FINGER1 22 | SLAYER_WEAR_FINGER2 23 | SLAYER_WEAR_FINGER3 24 | SLAYER_WEAR_FINGER4 25 | SLAYER_WEAR_FOOT 26 | SLAYER_WEAR_ZAP1 27 | SLAYER_WEAR_ZAP2 28 | SLAYER_WEAR_ZAP3 29 | SLAYER_WEAR_ZAP4 30 | SLAYER_WEAR_PDA 31 | SLAYER_WEAR_SHOULDER 32 | SLAYER_WEAR_MAX 33 | ) 34 | 35 | type Slayer struct { 36 | PlayerCreature 37 | 38 | Competence byte 39 | CompetenceShape byte 40 | 41 | Sex Sex_t 42 | HairStyle HairStyle 43 | 44 | HairColor Color_t 45 | SkinColor Color_t 46 | 47 | Alignment Alignment_t 48 | 49 | MP [3]MP_t 50 | 51 | Damage [3]Damage_t 52 | ToHit [2]ToHit_t 53 | Defense [2]Defense_t 54 | Protection [2]Protection_t 55 | AttackSpeed [2]Speed_t 56 | CriticalRatio int 57 | 58 | Vision [2]Vision_t 59 | SkillPoint SkillPoint_t 60 | 61 | DomainLevels [6]SkillLevel_t 62 | DomainExps [6]SkillExp_t 63 | HotKey [4]SkillType_t 64 | 65 | Fame Fame_t 66 | Gold Gold_t 67 | 68 | AttrBonus Bonus_t 69 | 70 | SkillDomainLevels [SKILL_DOMAIN_VAMPIRE]SkillLevel_t 71 | 72 | GoalExp [SKILL_DOMAIN_VAMPIRE]Exp_t 73 | 74 | // 技能糟 75 | SkillSlot map[SkillType_t]*SlayerSkillSlot 76 | 77 | WearItem [SLAYER_WEAR_MAX]*Item 78 | 79 | // 摩托车 80 | // Motocycle *Motocycle 81 | 82 | STRExp Exp_t 83 | DEXExp Exp_t 84 | INIExp Exp_t 85 | 86 | GuildName string 87 | 88 | HPStealAmount Steal_t 89 | HPStealRatio Steal_t 90 | MPStealAmount Steal_t 91 | MPStealRatio Steal_t 92 | 93 | HPRegen Regen_t 94 | MPRegen Regen_t 95 | 96 | Luck Luck_t 97 | 98 | MPRegenTime time.Time 99 | } 100 | 101 | func (slayer *Slayer) CreatureClass() CreatureClass { 102 | return CREATURE_CLASS_SLAYER 103 | } 104 | 105 | func (slayer *Slayer) PCInfo() data.PCInfo { 106 | ret := &data.PCSlayerInfo{ 107 | ObjectID: slayer.ObjectID, 108 | Name: slayer.Name, 109 | Sex: slayer.Sex, 110 | HairStyle: slayer.HairStyle, 111 | HairColor: slayer.HairColor, 112 | SkinColor: slayer.SkinColor, 113 | MasterEffectColor: slayer.MasterEffectColor, 114 | Alignment: slayer.Alignment, 115 | 116 | STRExp: slayer.STRExp, 117 | DEXExp: slayer.DEXExp, 118 | INIExp: slayer.INIExp, 119 | Rank: slayer.Rank, 120 | RankExp: slayer.RankExp, 121 | 122 | Fame: slayer.Fame, 123 | Gold: slayer.Gold, 124 | 125 | Sight: slayer.Sight, 126 | 127 | Competence: slayer.Competence, 128 | GuildID: slayer.GuildID, 129 | GuildName: slayer.GuildName, 130 | GuildMemberRank: slayer.GuildMemberRank, 131 | 132 | UnionID: slayer.UnionID, 133 | AdvancementLevel: slayer.AdvancementLevel, 134 | AdvancementGoalExp: slayer.AdvancementGoalExp, 135 | AttrBonus: slayer.AttrBonus, 136 | } 137 | 138 | for i := 0; i < 3; i++ { 139 | ret.STR[i] = slayer.STR[i] 140 | ret.DEX[i] = slayer.DEX[i] 141 | ret.INI[i] = slayer.INI[i] 142 | } 143 | 144 | for i := 0; i < 2; i++ { 145 | ret.HP[i] = slayer.HP[i] 146 | ret.MP[i] = slayer.MP[i] 147 | } 148 | 149 | for i := 0; i < 6; i++ { 150 | ret.DomainLevels[i] = slayer.DomainLevels[i] 151 | ret.DomainExps[i] = slayer.DomainExps[i] 152 | } 153 | 154 | for i := 0; i < 4; i++ { 155 | ret.HotKey[i] = slayer.HotKey[i] 156 | } 157 | 158 | return ret 159 | } 160 | 161 | // TODO 162 | func (slayer *Slayer) SkillInfo() data.PCSkillInfo { 163 | return nil 164 | } 165 | 166 | // TODO 167 | func (slayer *Slayer) computeDamage(creature CreatureInterface, bCritical bool) Damage_t { 168 | return 0 169 | } 170 | 171 | func (slayer *Slayer) getProtection() Protection_t { 172 | return slayer.Protection[ATTR_CURRENT] 173 | } 174 | 175 | // TODO 176 | func (slayer *Slayer) getWearItem(class int) ItemInterface { 177 | return nil 178 | } 179 | 180 | func (slayer *Slayer) getHP(attr int) HP_t { 181 | return slayer.HP[attr] 182 | } 183 | 184 | // TODO 185 | func increaseDomainExp(pSlayer *Slayer, 186 | SKILL_DOMAIN_BLADE SkillPoint_t, 187 | Point int, 188 | _GCAttackMeleeOK1 packet.Packet, 189 | level Level_t) { 190 | 191 | } 192 | -------------------------------------------------------------------------------- /gameserver/tile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/log" 5 | . "github.com/tiancaiamao/ouster/util" 6 | ) 7 | 8 | type TileFlags uint16 9 | 10 | const ( 11 | TILE_GROUND_BLOCKED = iota 12 | TILE_AIR_BLOCKED 13 | TILE_UNDERGROUND_BLOCKED 14 | TILE_WALKING_CREATURE 15 | TILE_FLYING_CREATURE 16 | TILE_BURROWING_CREATURE 17 | TILE_ITEM 18 | TILE_OBSTACLE 19 | TILE_EFFECT 20 | TILE_BUILDING 21 | TILE_PORTAL 22 | TILE_TERRAIN 23 | ) 24 | 25 | type Tile struct { 26 | Flags uint16 27 | Option uint16 28 | Objects map[ObjectID_t]ObjectInterface 29 | Sector *Sector 30 | } 31 | 32 | const SECTOR_SIZE = 13 33 | 34 | type Sector struct { 35 | Objects map[ObjectID_t]Object 36 | NearbySectors [9]*Sector 37 | } 38 | 39 | func (tile *Tile) SetBlocked(mode MoveMode) { 40 | tile.Flags |= (1 << mode) 41 | } 42 | 43 | // TODO 44 | func (tile *Tile) HasPortal() bool { 45 | return false 46 | } 47 | 48 | func (tile *Tile) getEffect(uint16) EffectInterface { 49 | // TODO 50 | return nil 51 | } 52 | 53 | // TODO 54 | func (time *Tile) deleteEffect(ObjectID_t) { 55 | } 56 | 57 | // TODO 58 | func (tile *Tile) hasEffect() bool { 59 | return false 60 | } 61 | 62 | func (tile *Tile) isGroundBlocked() bool { 63 | return (tile.Flags & (1 << TILE_GROUND_BLOCKED)) != 0 64 | } 65 | 66 | func (tile *Tile) IsAirBlocked() bool { 67 | return (tile.Flags & (1 << TILE_AIR_BLOCKED)) != 0 68 | } 69 | 70 | func (tile *Tile) isUndergroundBlocked() bool { 71 | return (tile.Flags & (1 << TILE_UNDERGROUND_BLOCKED)) != 0 72 | } 73 | 74 | func (tile *Tile) isFixedGroundBlocked() bool { 75 | return tile.isGroundBlocked() && !tile.hasWalkingCreature() 76 | } 77 | 78 | func (tile *Tile) hasWalkingCreature() bool { 79 | return tile.Flags&(1<getZone()) 173 | // TODO 174 | timeband := 0 175 | // pItem := vampire.getWearItem(Vampire_WEAR_RIGHTHAND) 176 | // 177 | // if pItem != nil { 178 | // MinDamage += pItem.getMinDamage() 179 | // MaxDamage += pItem.getMaxDamage() 180 | // } 181 | 182 | realDamage := max(1, int(minDamage)+rand.Intn(int(maxDamage-minDamage))) 183 | realDamage = getPercentValue(realDamage, VampireTimebandFactor[timeband]) 184 | 185 | var protection Protection_t 186 | switch creature.(type) { 187 | case *Vampire: 188 | protection = creature.(*Vampire).Protection[ATTR_CURRENT] 189 | protection = Protection_t(getPercentValue(int(protection), VampireTimebandFactor[timeband])) 190 | case *Monster: 191 | protection = creature.(*Monster).Protection 192 | protection = Protection_t(getPercentValue(int(protection), VampireTimebandFactor[timeband])) 193 | case *Slayer: 194 | protection = creature.(*Slayer).Protection[ATTR_CURRENT] 195 | case *Ouster: 196 | protection = creature.(*Ouster).Protection[ATTR_CURRENT] 197 | default: 198 | log.Errorln("输入的参数不对") 199 | } 200 | finalDamage := Damage_t(computeFinalDamage(minDamage, maxDamage, Damage_t(realDamage), protection, bCritical)) 201 | 202 | return finalDamage 203 | } 204 | -------------------------------------------------------------------------------- /gameserver/zoneinfo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "github.com/tiancaiamao/ouster/util" 5 | ) 6 | 7 | type ZoneInfo struct { 8 | IsPKZone bool `json:"isPkZone"` 9 | IsNoPortalZone bool `json:"isNoPortalZone"` 10 | IsMasterLair bool `json:"isMasterLair"` 11 | IsHolyLand bool `json:"isHolyLand"` 12 | SMPFileName string `json:"smpFileName"` 13 | SSIFileName string `json:"ssiFileName"` 14 | } 15 | 16 | // 用于维护一个从ZoneID到ZoneInfo的映射关系 17 | var gZoneInfoManager ZoneInfoManager 18 | 19 | type ZoneInfoManager map[ZoneID_t]*ZoneInfo 20 | 21 | func (zm ZoneInfoManager) GetZoneInfo(id ZoneID_t) *ZoneInfo { 22 | return zm[id] 23 | } 24 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "os" 8 | ) 9 | 10 | type LogLevel int 11 | 12 | const ( 13 | LOG_LEVEL_DEBUG LogLevel = iota 14 | LOG_LEVEL_INFO 15 | LOG_LEVEL_WARN 16 | LOG_LEVEL_ERROR 17 | ) 18 | 19 | var defaultLog *DefaultLogger 20 | 21 | type DefaultLogger struct { 22 | *log.Logger 23 | level LogLevel 24 | callstack int 25 | } 26 | 27 | func (l *DefaultLogger) SetLevel(level LogLevel) { 28 | l.level = level 29 | } 30 | 31 | func (l *DefaultLogger) LogLevel() LogLevel { 32 | return l.level 33 | } 34 | 35 | func (l *DefaultLogger) Debug(args ...interface{}) { 36 | if l.level <= LOG_LEVEL_DEBUG { 37 | l.SetPrefix("[DEBUG]") 38 | l.Output(l.callstack, fmt.Sprint(args...)) 39 | } 40 | } 41 | 42 | func (l *DefaultLogger) Debugln(args ...interface{}) { 43 | if l.level <= LOG_LEVEL_DEBUG { 44 | l.SetPrefix("[DEBUG]") 45 | l.Output(l.callstack, fmt.Sprintln(args...)) 46 | } 47 | } 48 | 49 | func (l *DefaultLogger) Debugf(format string, args ...interface{}) { 50 | if l.level <= LOG_LEVEL_DEBUG { 51 | l.SetPrefix("[DEBUG]") 52 | l.Output(l.callstack, fmt.Sprintf(format, args...)) 53 | } 54 | } 55 | 56 | func (l *DefaultLogger) Info(args ...interface{}) { 57 | if l.level <= LOG_LEVEL_INFO { 58 | l.SetPrefix("[INFO] ") 59 | l.Output(l.callstack, fmt.Sprint(args...)) 60 | } 61 | } 62 | 63 | func (l *DefaultLogger) Infoln(args ...interface{}) { 64 | if l.level <= LOG_LEVEL_INFO { 65 | l.SetPrefix("[INFO] ") 66 | l.Output(l.callstack, fmt.Sprintln(args...)) 67 | } 68 | } 69 | 70 | func (l *DefaultLogger) Infof(format string, args ...interface{}) { 71 | if l.level <= LOG_LEVEL_INFO { 72 | l.SetPrefix("[INFO] ") 73 | l.Output(l.callstack, fmt.Sprintf(format, args...)) 74 | } 75 | } 76 | 77 | func (l *DefaultLogger) Warn(args ...interface{}) { 78 | if l.level <= LOG_LEVEL_WARN { 79 | l.SetPrefix("[WARN] ") 80 | l.Output(l.callstack, fmt.Sprint(args...)) 81 | } 82 | } 83 | 84 | func (l *DefaultLogger) Warnln(args ...interface{}) { 85 | if l.level <= LOG_LEVEL_WARN { 86 | l.SetPrefix("[WARN] ") 87 | l.Output(l.callstack, fmt.Sprintln(args...)) 88 | } 89 | } 90 | 91 | func (l *DefaultLogger) Warnf(format string, args ...interface{}) { 92 | if l.level <= LOG_LEVEL_WARN { 93 | l.SetPrefix("[WARN] ") 94 | l.Output(l.callstack, fmt.Sprintf(format, args...)) 95 | } 96 | } 97 | 98 | func (l *DefaultLogger) Error(args ...interface{}) { 99 | if l.level <= LOG_LEVEL_ERROR { 100 | l.SetPrefix("[ERROR]") 101 | l.Output(l.callstack, fmt.Sprint(args...)) 102 | } 103 | } 104 | 105 | func (l *DefaultLogger) Errorln(args ...interface{}) { 106 | if l.level <= LOG_LEVEL_ERROR { 107 | l.SetPrefix("[ERROR]") 108 | l.Output(l.callstack, fmt.Sprintln(args...)) 109 | } 110 | } 111 | 112 | func (l *DefaultLogger) Errorf(format string, args ...interface{}) { 113 | if l.level <= LOG_LEVEL_ERROR { 114 | l.SetPrefix("[ERROR]") 115 | l.Output(l.callstack, fmt.Sprintf(format, args...)) 116 | } 117 | } 118 | 119 | func New(out io.Writer, prefix string, flag int, lv LogLevel) *DefaultLogger { 120 | return &DefaultLogger{ 121 | Logger: log.New(out, prefix, flag), 122 | level: lv, 123 | callstack: 2, 124 | } 125 | } 126 | 127 | func init() { 128 | defaultLog = New(os.Stderr, "", log.LstdFlags|log.Lshortfile, LOG_LEVEL_DEBUG) 129 | defaultLog.callstack = 3 130 | } 131 | 132 | func SetLevel(level LogLevel) { 133 | defaultLog.SetLevel(level) 134 | } 135 | 136 | func Level() LogLevel { 137 | return defaultLog.LogLevel() 138 | } 139 | 140 | func Debug(args ...interface{}) { 141 | defaultLog.Debug(args...) 142 | } 143 | 144 | func Debugln(args ...interface{}) { 145 | defaultLog.Debugln(args...) 146 | } 147 | 148 | func Debugf(format string, args ...interface{}) { 149 | defaultLog.Debugf(format, args...) 150 | } 151 | 152 | func Info(args ...interface{}) { 153 | defaultLog.Info(args...) 154 | } 155 | 156 | func Infoln(args ...interface{}) { 157 | defaultLog.Infoln(args...) 158 | } 159 | 160 | func Infof(format string, args ...interface{}) { 161 | defaultLog.Infof(format, args...) 162 | } 163 | 164 | func Warn(args ...interface{}) { 165 | defaultLog.Warn(args...) 166 | } 167 | 168 | func Warnln(args ...interface{}) { 169 | defaultLog.Warnln(args...) 170 | } 171 | 172 | func Warnf(format string, args ...interface{}) { 173 | defaultLog.Warnf(format, args...) 174 | } 175 | 176 | func Error(args ...interface{}) { 177 | defaultLog.Error(args...) 178 | } 179 | 180 | func Errorln(args ...interface{}) { 181 | defaultLog.Error(args...) 182 | } 183 | 184 | func Errorf(format string, args ...interface{}) { 185 | defaultLog.Errorf(format, args...) 186 | } -------------------------------------------------------------------------------- /loginserver/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tiancaiamao/ouster/config" 5 | "github.com/tiancaiamao/ouster/log" 6 | "github.com/tiancaiamao/ouster/packet" 7 | "net" 8 | ) 9 | 10 | func main() { 11 | ln, err := net.Listen("tcp", config.LoginServerPort) 12 | if err != nil { 13 | panic(err) 14 | } 15 | log.Infoln("loginserver started") 16 | for { 17 | conn, err := ln.Accept() 18 | if err != nil { 19 | log.Errorln("accept err:", err) 20 | continue 21 | } 22 | 23 | log.Infoln("receive a connect request") 24 | go serve(conn) 25 | } 26 | } 27 | 28 | func serve(conn net.Conn) { 29 | defer conn.Close() 30 | 31 | reader := packet.NewReader() 32 | writer := packet.NewWriter() 33 | 34 | for { 35 | pkt, err := reader.Read(conn) 36 | if err != nil { 37 | if _, ok := err.(packet.NotImplementError); !ok { 38 | log.Errorln("read packet error in loginserver's serve:", err) 39 | return 40 | } 41 | } 42 | 43 | log.Debugln("read a packet: ", pkt.PacketID()) 44 | 45 | switch pkt.PacketID() { 46 | case packet.PACKET_CL_GET_WORLD_LIST: 47 | writer.Write(conn, packet.LCWorldListPacket{}) 48 | case packet.PACKET_CL_LOGIN: 49 | writer.Write(conn, packet.LCLoginOKPacket{}) 50 | case packet.PACKET_CL_SELECT_SERVER: 51 | writer.Write(conn, &packet.LCPCListPacket{}) 52 | case packet.PACKET_CL_SELECT_WORLD: 53 | writer.Write(conn, &packet.LCServerListPacket{}) 54 | case packet.PACKET_CL_VERSION_CHECK: 55 | writer.Write(conn, packet.LCVersionCheckOKPacket{}) 56 | case packet.PACKET_CL_SELECT_PC: 57 | reconnect := &packet.LCReconnectPacket{ 58 | Ip: config.GameServerIP, 59 | Port: 9998, 60 | Key: 82180, 61 | } 62 | writer.Write(conn, reconnect) 63 | return 64 | default: 65 | log.Errorf("get a unknow packet: %d\n", pkt.PacketID()) 66 | } 67 | } 68 | } 69 | 70 | // func Debug(writer packet.Writer, pkt packet.Packet) { 71 | // stdout := &bytes.Buffer{} 72 | // writer.Write(stdout, pkt) 73 | // log.Println(stdout.Bytes()) 74 | // } 75 | -------------------------------------------------------------------------------- /packet/api.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "github.com/tiancaiamao/ouster/log" 7 | "io" 8 | ) 9 | 10 | type PacketID uint16 11 | 12 | type Packet interface { 13 | PacketID() PacketID 14 | PacketSize() uint32 15 | Read(reader io.Reader, code uint8) error 16 | Write(writer io.Writer, code uint8) error 17 | } 18 | 19 | func Write(writer io.Writer, pkt Packet, code uint8, seq uint8) error { 20 | id := pkt.PacketID() 21 | err := binary.Write(writer, binary.LittleEndian, id) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | sz := pkt.PacketSize() 27 | err = binary.Write(writer, binary.LittleEndian, sz) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | err = binary.Write(writer, binary.LittleEndian, seq) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | err = pkt.Write(writer, code) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func Read(reader io.Reader, code uint8) (ret Packet, seq uint8, err error) { 46 | var id PacketID 47 | var sz uint32 48 | 49 | err = ReadHeader(reader, &id, &sz, &seq) 50 | if err != nil { 51 | return 52 | } 53 | 54 | if id >= PACKET_MAX { 55 | err = errors.New("packet id too large!") 56 | return 57 | } 58 | 59 | ret = packetTable[id] 60 | if ret == nil { 61 | log.Debugln("reading a not implement packet:", id) 62 | var buf [500]byte 63 | raw := RawPacket{ 64 | Id: id, 65 | Seq: seq, 66 | } 67 | if sz > uint32(len(buf)) { 68 | err = errors.New("too large raw packet") 69 | return 70 | } 71 | _, err = reader.Read(buf[:sz]) 72 | if err != nil { 73 | return 74 | } 75 | copy(raw.Data, buf[:sz]) 76 | err = NotImplementError{} 77 | return 78 | } 79 | 80 | err = ret.Read(reader, code) 81 | return 82 | } 83 | -------------------------------------------------------------------------------- /packet/cg.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | // "github.com/tiancaiamao/ouster/log" 8 | . "github.com/tiancaiamao/ouster/util" 9 | "io" 10 | ) 11 | 12 | type CGConnectPacket struct { 13 | NotImplementWrite 14 | 15 | Key uint32 16 | PCType uint8 17 | PCName string 18 | MacAddress [4]byte 19 | } 20 | 21 | func (connect *CGConnectPacket) PacketID() PacketID { 22 | return PACKET_CG_CONNECT 23 | } 24 | 25 | func (connect *CGConnectPacket) MarshalBinary(code uint8) ([]byte, error) { 26 | buf := &bytes.Buffer{} 27 | binary.Write(buf, binary.LittleEndian, connect.Key) 28 | binary.Write(buf, binary.LittleEndian, connect.PCType) 29 | binary.Write(buf, binary.LittleEndian, uint8(len(connect.PCName))) 30 | io.WriteString(buf, connect.PCName) 31 | buf.Write(connect.MacAddress[:]) 32 | return buf.Bytes(), nil 33 | } 34 | 35 | func (packet *CGConnectPacket) Read(reader io.Reader, code uint8) error { 36 | binary.Read(reader, binary.LittleEndian, &packet.Key) 37 | binary.Read(reader, binary.LittleEndian, &packet.PCType) 38 | var szName uint8 39 | var buf [256]byte 40 | binary.Read(reader, binary.LittleEndian, &szName) 41 | _, err := reader.Read(buf[:szName]) 42 | if err != nil { 43 | return err 44 | } 45 | packet.PCName = string(buf[:szName]) 46 | _, err = reader.Read(packet.MacAddress[:]) 47 | if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | 53 | type CGReadyPacket struct { 54 | NotImplementWrite 55 | } 56 | 57 | func (ready CGReadyPacket) PacketID() PacketID { 58 | return PACKET_CG_READY 59 | } 60 | func (ready *CGReadyPacket) Read(reader io.Reader, code uint8) error { 61 | return nil 62 | } 63 | 64 | type CGMovePacket struct { 65 | NotImplementWrite 66 | 67 | Dir uint8 68 | X uint8 69 | Y uint8 70 | } 71 | 72 | func (move CGMovePacket) PacketID() PacketID { 73 | return PACKET_CG_MOVE 74 | } 75 | 76 | const ( 77 | dirLEFT = 0 78 | dirRIGHT = 4 79 | dirUP = 6 80 | dirDOWN = 2 81 | dirLEFTUP = 7 82 | dirRIGHTUP = 5 83 | dirLEFTDOWN = 1 84 | dirRIGHTDOWN = 3 85 | ) 86 | 87 | func encryptDir(dir byte) (uint8, error) { 88 | var ret uint8 89 | switch dir { 90 | case 53: 91 | dir = dirLEFT 92 | case 49: 93 | dir = dirRIGHT 94 | case 51: 95 | dir = dirUP 96 | case 55: 97 | dir = dirDOWN 98 | case 50: 99 | dir = dirLEFTUP 100 | case 48: 101 | dir = dirRIGHTUP 102 | case 52: 103 | dir = dirLEFTDOWN 104 | case 54: 105 | dir = dirRIGHTDOWN 106 | default: 107 | return ret, errors.New("unknow dir") 108 | } 109 | return ret, nil 110 | } 111 | 112 | func SHUFFLE_STATEMENT_3(code uint8, A func(), B func(), C func()) { 113 | switch code % 3 { 114 | case 0: 115 | A() 116 | B() 117 | C() 118 | case 1: 119 | B() 120 | C() 121 | A() 122 | case 2: 123 | C() 124 | A() 125 | B() 126 | } 127 | return 128 | } 129 | 130 | func SHUFFLE_STATEMENT_2(code uint8, A func(), B func()) { 131 | switch code % 2 { 132 | case 0: 133 | A() 134 | B() 135 | case 1: 136 | B() 137 | A() 138 | } 139 | return 140 | } 141 | 142 | func SHUFFLE_STATEMENT_4(code uint8, A func(), B func(), C func(), D func()) { 143 | switch code % 4 { 144 | case 0: 145 | A() 146 | B() 147 | C() 148 | D() 149 | case 1: 150 | B() 151 | C() 152 | D() 153 | A() 154 | case 2: 155 | C() 156 | D() 157 | A() 158 | B() 159 | case 3: 160 | D() 161 | A() 162 | C() 163 | B() 164 | } 165 | return 166 | } 167 | 168 | func (move *CGMovePacket) Read(reader io.Reader, code uint8) error { 169 | A := func() { 170 | binary.Read(reader, binary.LittleEndian, &move.X) 171 | move.X ^= code 172 | } 173 | B := func() { 174 | binary.Read(reader, binary.LittleEndian, &move.Y) 175 | move.Y ^= code 176 | } 177 | C := func() { 178 | binary.Read(reader, binary.LittleEndian, &move.Dir) 179 | move.Dir ^= code 180 | } 181 | // encryption...fuck 182 | SHUFFLE_STATEMENT_3(code, A, B, C) 183 | if move.Dir >= 8 { 184 | return errors.New("Dir out of range") 185 | } 186 | return nil 187 | } 188 | 189 | type CGVerifyTimePacket struct { 190 | NotImplementWrite 191 | } 192 | 193 | func (verifyTime CGVerifyTimePacket) PacketID() PacketID { 194 | return PACKET_CG_VERIFY_TIME 195 | } 196 | 197 | func (verifyTime *CGVerifyTimePacket) Read(reader io.Reader, code uint8) error { 198 | return nil 199 | } 200 | 201 | type CGAttackPacket struct { 202 | NotImplementWrite 203 | 204 | ObjectID ObjectID_t 205 | X uint8 206 | Y uint8 207 | Dir uint8 208 | } 209 | 210 | func (attack CGAttackPacket) PacketID() PacketID { 211 | return PACKET_CG_ATTACK 212 | } 213 | 214 | func (attack *CGAttackPacket) Read(reader io.Reader, code uint8) error { 215 | // [188 251 55 82 48 0 0] 216 | A := func() { 217 | binary.Read(reader, binary.LittleEndian, &attack.ObjectID) 218 | attack.ObjectID ^= ObjectID_t(code) 219 | } 220 | B := func() { 221 | binary.Read(reader, binary.LittleEndian, &attack.X) 222 | attack.X ^= code 223 | } 224 | C := func() { 225 | binary.Read(reader, binary.LittleEndian, &attack.Y) 226 | attack.Y ^= code 227 | } 228 | D := func() { 229 | binary.Read(reader, binary.LittleEndian, &attack.Dir) 230 | attack.Dir ^= code 231 | } 232 | SHUFFLE_STATEMENT_4(code, A, B, C, D) 233 | return nil 234 | } 235 | 236 | type CGBloodDrainPacket struct { 237 | NotImplementWrite 238 | 239 | ObjectID uint32 240 | } 241 | 242 | func (bloodDrain CGBloodDrainPacket) PacketID() PacketID { 243 | return PACKET_CG_BLOOD_DRAIN 244 | } 245 | func (bloodDrain CGBloodDrainPacket) String() string { 246 | return "blood drain" 247 | } 248 | func (packet *CGBloodDrainPacket) Read(reader io.Reader, code uint8) error { 249 | return binary.Read(reader, binary.LittleEndian, &packet.ObjectID) 250 | } 251 | 252 | type CGLearnSkillPacket struct { 253 | NotImplementWrite 254 | 255 | SkillType SkillType_t 256 | SkillDomainType uint8 257 | } 258 | 259 | func (learnSkill CGLearnSkillPacket) PacketID() PacketID { 260 | return PACKET_CG_LEARN_SKILL 261 | } 262 | 263 | func (learnSkill CGLearnSkillPacket) String() string { 264 | return "learn skill" 265 | } 266 | 267 | func (learn *CGLearnSkillPacket) Read(reader io.Reader, code uint8) error { 268 | binary.Read(reader, binary.LittleEndian, &learn.SkillType) 269 | binary.Read(reader, binary.LittleEndian, &learn.SkillDomainType) 270 | return nil 271 | } 272 | 273 | type CGSkillToObjectPacket struct { 274 | NotImplementWrite 275 | 276 | SkillType SkillType_t 277 | CEffectID uint16 278 | TargetObjectID ObjectID_t 279 | } 280 | 281 | func (skill CGSkillToObjectPacket) PacketID() PacketID { 282 | return PACKET_CG_SKILL_TO_OBJECT 283 | } 284 | 285 | func (skill CGSkillToObjectPacket) String() string { 286 | return "skill to object" 287 | } 288 | 289 | func (ret *CGSkillToObjectPacket) Read(reader io.Reader, code uint8) error { 290 | A := func() { 291 | binary.Read(reader, binary.LittleEndian, &ret.SkillType) 292 | ret.SkillType ^= SkillType_t(code) 293 | } 294 | B := func() { 295 | binary.Read(reader, binary.LittleEndian, &ret.CEffectID) 296 | ret.CEffectID ^= uint16(code) 297 | } 298 | C := func() { 299 | binary.Read(reader, binary.LittleEndian, &ret.TargetObjectID) 300 | ret.TargetObjectID ^= ObjectID_t(code) 301 | } 302 | SHUFFLE_STATEMENT_3(code, A, B, C) 303 | return nil 304 | } 305 | 306 | type CGSkillToSelfPacket struct { 307 | NotImplementWrite 308 | 309 | SkillType SkillType_t 310 | CEffectID uint16 311 | } 312 | 313 | func (skill CGSkillToSelfPacket) PacketID() PacketID { 314 | return PACKET_CG_SKILL_TO_SELF 315 | } 316 | 317 | func (skill CGSkillToSelfPacket) String() string { 318 | return "skill to self" 319 | } 320 | 321 | func (ret *CGSkillToSelfPacket) Read(reader io.Reader, code uint8) error { 322 | A := func() { 323 | binary.Read(reader, binary.LittleEndian, &ret.SkillType) 324 | ret.SkillType ^= SkillType_t(code) 325 | } 326 | B := func() { 327 | binary.Read(reader, binary.LittleEndian, &ret.CEffectID) 328 | ret.CEffectID ^= uint16(code) 329 | } 330 | SHUFFLE_STATEMENT_2(code, A, B) 331 | return nil 332 | } 333 | 334 | type CGSkillToTilePacket struct { 335 | NotImplementWrite 336 | 337 | SkillType SkillType_t 338 | CEffectID uint16 339 | X Coord_t 340 | Y Coord_t 341 | } 342 | 343 | func (skill CGSkillToTilePacket) PacketID() PacketID { 344 | return PACKET_CG_SKILL_TO_TILE 345 | } 346 | 347 | func (skill CGSkillToTilePacket) String() string { 348 | return "skill to tile" 349 | } 350 | 351 | func (ret *CGSkillToTilePacket) Read(reader io.Reader, code uint8) error { 352 | A := func() { 353 | binary.Read(reader, binary.LittleEndian, &ret.SkillType) 354 | ret.SkillType ^= SkillType_t(code) 355 | } 356 | B := func() { 357 | binary.Read(reader, binary.LittleEndian, &ret.CEffectID) 358 | ret.CEffectID ^= uint16(code) 359 | } 360 | C := func() { 361 | binary.Read(reader, binary.LittleEndian, &ret.X) 362 | ret.X ^= Coord_t(code) 363 | } 364 | D := func() { 365 | binary.Read(reader, binary.LittleEndian, &ret.Y) 366 | ret.Y ^= Coord_t(code) 367 | } 368 | SHUFFLE_STATEMENT_4(code, A, B, C, D) 369 | return nil 370 | } 371 | 372 | type CGSayPacket struct { 373 | NotImplementWrite 374 | 375 | Color uint32 376 | Message string 377 | } 378 | 379 | func (say *CGSayPacket) PacketID() PacketID { 380 | return PACKET_CG_SAY 381 | } 382 | func (say *CGSayPacket) String() string { 383 | return "say" 384 | } 385 | func (say *CGSayPacket) Read(reader io.Reader, code uint8) error { 386 | binary.Read(reader, binary.LittleEndian, &say.Color) 387 | var sz uint8 388 | var buf [256]byte 389 | binary.Read(reader, binary.LittleEndian, &sz) 390 | _, err := reader.Read(buf[:sz]) 391 | if err != nil { 392 | return err 393 | } 394 | say.Message = string(buf[:sz]) 395 | return nil 396 | } 397 | 398 | type CGLogoutPacket struct { 399 | NotImplementWrite 400 | } 401 | 402 | func (_ CGLogoutPacket) PacketID() PacketID { 403 | return PACKET_CG_LOGOUT 404 | } 405 | 406 | func (_ *CGLogoutPacket) Read(reader io.Reader, code uint8) error { 407 | return nil 408 | } 409 | 410 | func (_ CGLogoutPacket) String() string { 411 | return "logout" 412 | } 413 | -------------------------------------------------------------------------------- /packet/cl.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | type NotImplementWrite struct{} 10 | 11 | func (ign NotImplementWrite) Write(writer io.Writer, code uint8) error { 12 | return errors.New("not implement write method!!") 13 | } 14 | 15 | func (ign NotImplementWrite) PacketSize() uint32 { 16 | return 0 17 | } 18 | 19 | type CLLoginPacket struct { 20 | NotImplementWrite 21 | Username string 22 | Password string 23 | MacAddress [6]byte 24 | LoginMode uint8 25 | } 26 | 27 | func (login *CLLoginPacket) PacketID() PacketID { 28 | return PACKET_CL_LOGIN 29 | } 30 | 31 | func (login *CLLoginPacket) Read(reader io.Reader, code uint8) error { 32 | var tmp uint8 33 | var buf [256]byte 34 | binary.Read(reader, binary.LittleEndian, &tmp) 35 | _, err := reader.Read(buf[:tmp]) 36 | if err != nil { 37 | return err 38 | } 39 | login.Username = string(buf[:tmp]) 40 | 41 | binary.Read(reader, binary.LittleEndian, &tmp) 42 | _, err = reader.Read(buf[:tmp]) 43 | if err != nil { 44 | return err 45 | } 46 | login.Password = string(buf[:tmp]) 47 | 48 | reader.Read(login.MacAddress[:]) 49 | binary.Read(reader, binary.LittleEndian, &login.LoginMode) 50 | return nil 51 | } 52 | 53 | type CLVersionCheckPacket struct { 54 | NotImplementWrite 55 | } 56 | 57 | func (ign *CLVersionCheckPacket) Read(reader io.Reader, code uint8) error { 58 | var buf [4]byte 59 | reader.Read(buf[:]) 60 | return nil 61 | } 62 | 63 | func (v CLVersionCheckPacket) PacketID() PacketID { 64 | return PACKET_CL_VERSION_CHECK 65 | } 66 | 67 | type CLGetWorldListPacket struct { 68 | NotImplementWrite 69 | } 70 | 71 | func (worldList CLGetWorldListPacket) PacketID() PacketID { 72 | return PACKET_CL_GET_WORLD_LIST 73 | } 74 | 75 | func (ign *CLGetWorldListPacket) Read(reader io.Reader, code uint8) error { 76 | return nil 77 | } 78 | 79 | type CLSelectWorldPacket struct { 80 | NotImplementWrite 81 | 82 | WorldID uint8 83 | } 84 | 85 | func (sw CLSelectWorldPacket) PacketID() PacketID { 86 | return PACKET_CL_SELECT_WORLD 87 | } 88 | 89 | func (v *CLSelectWorldPacket) Read(reader io.Reader, code uint8) error { 90 | binary.Read(reader, binary.LittleEndian, &v.WorldID) 91 | return nil 92 | } 93 | 94 | type CLSelectServerPacket struct { 95 | NotImplementWrite 96 | Data uint8 97 | } 98 | 99 | func (ss CLSelectServerPacket) PacketID() PacketID { 100 | return PACKET_CL_SELECT_SERVER 101 | } 102 | 103 | func (v *CLSelectServerPacket) Read(reader io.Reader, code uint8) error { 104 | binary.Read(reader, binary.LittleEndian, &v.Data) 105 | return nil 106 | } 107 | 108 | type PCType uint8 109 | type CLSelectPcPacket struct { 110 | NotImplementWrite 111 | Name string 112 | Type PCType 113 | } 114 | 115 | func (sp *CLSelectPcPacket) PacketID() PacketID { 116 | return PACKET_CL_SELECT_PC 117 | } 118 | func (_ *CLSelectPcPacket) PacketSize() uint32 { 119 | return 0 120 | } 121 | 122 | func (ret *CLSelectPcPacket) Read(reader io.Reader, code uint8) error { 123 | // [8 178 187 212 217 209 218 202 206 0] 124 | var sz uint8 125 | var buf [256]byte 126 | binary.Read(reader, binary.LittleEndian, &sz) 127 | _, err := reader.Read(buf[:sz]) 128 | if err != nil { 129 | return err 130 | } 131 | ret.Name = string(buf[:sz]) 132 | binary.Read(reader, binary.LittleEndian, &ret.Type) 133 | return nil 134 | } 135 | -------------------------------------------------------------------------------- /packet/lc.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "github.com/tiancaiamao/ouster/data" 7 | "io" 8 | ) 9 | 10 | type LCLoginOKPacket struct { 11 | DummyRead 12 | } 13 | 14 | func (loginOk LCLoginOKPacket) PacketID() PacketID { 15 | return PACKET_LC_LOGIN_OK 16 | } 17 | func (loginOk LCLoginOKPacket) PacketSize() uint32 { 18 | return 5 19 | } 20 | 21 | func (loginOk LCLoginOKPacket) String() string { 22 | return "loginOK" 23 | } 24 | 25 | func (loginOk LCLoginOKPacket) Write(buf io.Writer, code uint8) error { 26 | buf.Write([]byte{0, 0, 231, 254, 255}) 27 | return nil 28 | } 29 | 30 | type LCVersionCheckOKPacket struct { 31 | DummyRead 32 | } 33 | 34 | func (v LCVersionCheckOKPacket) PacketID() PacketID { 35 | return PACKET_LC_VERSION_CHECK_OK 36 | } 37 | func (v LCVersionCheckOKPacket) PacketSize() uint32 { 38 | return 0 39 | } 40 | func (v LCVersionCheckOKPacket) String() string { 41 | return "version check ok" 42 | } 43 | func (v LCVersionCheckOKPacket) Write(buf io.Writer, code uint8) error { 44 | return nil 45 | } 46 | 47 | type LCWorldListPacket struct { 48 | DummyRead 49 | } 50 | 51 | func (wl LCWorldListPacket) PacketSize() uint32 { 52 | return 13 53 | } 54 | func (wl LCWorldListPacket) PacketID() PacketID { 55 | return PACKET_LC_WORLD_LIST 56 | } 57 | func (wl LCWorldListPacket) String() string { 58 | return "world list" 59 | } 60 | func (wl LCWorldListPacket) Write(buf io.Writer, code uint8) error { 61 | buf.Write([]byte{1, 1, 1, 8, 185, 237, 247, 200, 193, 182, 211, 252, 0}) 62 | return nil 63 | } 64 | 65 | type LCServerListPacket struct { 66 | DummyRead 67 | 68 | CurrentWorld uint8 69 | Size uint8 70 | list []string 71 | } 72 | 73 | func (sl *LCServerListPacket) PacketID() PacketID { 74 | return PACKET_LC_SERVER_LIST 75 | } 76 | func (sl *LCServerListPacket) String() string { 77 | return "server list" 78 | } 79 | func (sl *LCServerListPacket) PacketSize() uint32 { 80 | return 22 81 | } 82 | func (sl *LCServerListPacket) Write(buf io.Writer, code uint8) error { 83 | buf.Write([]byte{1, 2, 0, 6, 183, 226, 178, 226, 199, 248, 0, 1, 8, 185, 237, 247, 200, 193, 182, 211, 252, 0}) 84 | return nil 85 | } 86 | 87 | type LCPCListPacket struct { 88 | DummyRead 89 | list []data.PCInfo 90 | } 91 | 92 | func (pl *LCPCListPacket) PacketID() PacketID { 93 | return PACKET_LC_PC_LIST 94 | } 95 | func (pl *LCPCListPacket) String() string { 96 | return "pc list" 97 | } 98 | 99 | // TODO 100 | func (pl *LCPCListPacket) PacketSize() uint32 { 101 | buf := &bytes.Buffer{} 102 | pl.Write(buf, 0) 103 | return uint32(buf.Len()) 104 | } 105 | func (pl *LCPCListPacket) Write(buf io.Writer, code uint8) error { 106 | buf.Write([]byte{ 107 | 83, 79, 86, 108 | 6, 's', 'l', 'a', 'y', 'e', 'r', 0, 109 | 76, 29, 0, 0, 9, 0, 11, 0, 10, 0, 50, 170, 9, 0, 0, 53, 15, 0, 0, 47, 12, 0, 0, 18, 0, 18, 0, 20, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 144, 1, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 110 | 6, 'o', 'u', 's', 't', 'e', 'r', 1, 76, 29, 0, 0, 0, 121, 1, 101, 0, 121, 1, 0, 0, 8, 10, 0, 25, 0, 10, 0, 59, 1, 59, 1, 0, 0, 0, 0, 150, 50, 0, 0, 0, 0, 68, 0, 0, 0, 15, 39, 15, 39, 100, 111 | 7, 'v', 'a', 'm', 'p', 'i', 'r', 'e', 2, 76, 29, 0, 0, 0, 0, 0, 164, 1, 1, 121, 1, 20, 0, 20, 0, 20, 0, 216, 1, 216, 1, 150, 50, 125, 0, 0, 0, 217, 0, 0, 0, 15, 39, 100, 112 | }) 113 | return nil 114 | } 115 | 116 | type LCReconnectPacket struct { 117 | NotImplementWrite 118 | 119 | Ip string 120 | Port uint32 121 | Key uint32 122 | } 123 | 124 | func (rc *LCReconnectPacket) PacketID() PacketID { 125 | return PACKET_LC_RECONNECT 126 | } 127 | func (rc *LCReconnectPacket) PacketSize() uint32 { 128 | return uint32(1 + len(rc.Ip) + 8) 129 | } 130 | func (rc *LCReconnectPacket) String() string { 131 | return "reconnect" 132 | } 133 | func (rc *LCReconnectPacket) Write(buf io.Writer, code uint8) error { 134 | //[13 49 57 50 46 49 54 56 46 49 46 49 50 51 14 39 0 0 0 32 6 11] 135 | binary.Write(buf, binary.LittleEndian, uint8(len(rc.Ip))) 136 | io.WriteString(buf, rc.Ip) 137 | binary.Write(buf, binary.LittleEndian, rc.Port) 138 | binary.Write(buf, binary.LittleEndian, rc.Key) 139 | return nil 140 | } 141 | 142 | func (ret *LCReconnectPacket) Read(reader io.Reader, code uint8) error { 143 | var sz uint8 144 | var buf [256]byte 145 | binary.Read(reader, binary.LittleEndian, &sz) 146 | _, err := reader.Read(buf[:sz]) 147 | if err != nil { 148 | return err 149 | } 150 | ret.Ip = string(buf[:sz]) 151 | binary.Read(reader, binary.LittleEndian, &ret.Port) 152 | binary.Read(reader, binary.LittleEndian, &ret.Key) 153 | return nil 154 | } 155 | -------------------------------------------------------------------------------- /packet/marshal_test.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "bytes" 5 | "github.com/tiancaiamao/ouster/data" 6 | . "github.com/tiancaiamao/ouster/util" 7 | "testing" 8 | ) 9 | 10 | func TestPCOusterInfo(t *testing.T) { 11 | info := data.PCOusterInfo{ 12 | ObjectID: 12202, 13 | Name: "ouster", 14 | Level: 150, 15 | Sex: FEMALE, 16 | HairColor: 101, 17 | MasterEffectColor: 0, 18 | Alignment: 7500, 19 | STR: [3]Attr_t{10, 10, 10}, 20 | DEX: [3]Attr_t{25, 25, 25}, 21 | INI: [3]Attr_t{10, 10, 10}, 22 | HP: [2]HP_t{315, 315}, 23 | MP: [2]MP_t{186, 111}, 24 | Rank: 50, 25 | RankExp: 10700, 26 | Exp: 125, 27 | Fame: 500, 28 | Gold: 92, 29 | Sight: 13, 30 | Bonus: 9999, 31 | SkillBonus: 9994, 32 | SilverDamage: 0, 33 | Competence: 1, 34 | GuildID: 66, 35 | GuildName: "", 36 | GuildMemberRank: 4, 37 | UnionID: 0, 38 | AdvancementLevel: 100, 39 | AdvancementGoalExp: 0, 40 | } 41 | buf := &bytes.Buffer{} 42 | info.Write(buf) 43 | 44 | get := buf.Bytes() 45 | want := []byte{170, 47, 0, 0, 6, 'o', 'u', 's', 't', 'e', 'r', 150, 0, 101, 0, 0, 76, 29, 0, 0, 10, 0, 10, 0, 10, 0, 25, 0, 25, 0, 25, 0, 10, 0, 10, 0, 10, 0, 59, 1, 59, 1, 186, 0, 111, 0, 50, 204, 41, 0, 0, 125, 0, 0, 0, 244, 1, 0, 0, 92, 0, 0, 0, 13, 15, 39, 10, 39, 0, 0, 1, 66, 0, 0, 4, 0, 0, 0, 0, 100, 0, 0, 0, 0} 46 | if !bytes.Equal(want, get) { 47 | t.Errorf("want: %v\n, get:%v\n", want, get) 48 | } 49 | } 50 | 51 | func TestGCUpdateInfo(t *testing.T) { 52 | } 53 | -------------------------------------------------------------------------------- /packet/packet_test.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "bytes" 5 | "github.com/tiancaiamao/ouster/data" 6 | "io" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestGCUpdateInfoPacket(t *testing.T) { 12 | // raw := []byte{83, 221, 118, 0, 0, 4, 203, 171, 208, 222, 1, 1, 125, 1, 224, 1, 0, 76, 29, 0, 0, 13, 0, 13, 0, 13, 0, 14, 0, 14, 0, 14, 0, 20, 0, 20, 0, 20, 0, 1, 204, 41, 0, 0, 248, 4, 0, 0, 221, 5, 0, 0, 211, 15, 0, 0, 54, 0, 54, 0, 50, 0, 50, 0, 0, 0, 0, 0, 244, 1, 0, 0, 0, 50, 0, 0, 0, 0, 50, 0, 0, 0, 0, 40, 0, 0, 0, 7, 102, 3, 0, 0, 0, 30, 0, 0, 0, 0, 64, 66, 15, 0, 13, 114, 73, 57, 0, 0, 0, 72, 215, 1, 99, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 222, 118, 0, 0, 35, 0, 0, 0, 184, 11, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 223, 118, 0, 0, 22, 0, 0, 0, 172, 13, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 224, 118, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 20, 0, 0, 0, 4, 0, 225, 118, 0, 0, 15, 0, 0, 0, 124, 21, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 226, 118, 0, 0, 14, 0, 0, 0, 148, 17, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 4, 3, 227, 118, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0, 0, 9, 4, 228, 118, 0, 0, 1, 5, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0, 0, 9, 5, 4, 229, 118, 0, 0, 11, 0, 0, 0, 215, 5, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 2, 230, 118, 0, 0, 17, 0, 0, 0, 184, 9, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 3, 230, 118, 0, 0, 17, 0, 0, 0, 184, 9, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 4, 231, 118, 0, 0, 12, 0, 0, 0, 172, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 64, 0, 17, 232, 200, 7, 1, 17, 2, 49, 4, 1, 12, 15, 4, 3, 94, 0, 96, 0, 56, 0, 4, 5, 0, 6, 0, 7, 0, 8, 0, 3, 4, 194, 229, 177, 246, 172, 1, 17, 0, 222, 0, 6, 196, 171, 184, 163, 184, 224, 174, 1, 36, 0, 228, 0, 8, 199, 193, 183, 185, 181, 229, 184, 175, 158, 0, 23, 0, 235, 0, 0, 17, 0, 0, 0, 0, 152, 42, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 52, 1, 5, 0, 0, 0, 1, 0, 221, 118, 0, 0} 13 | raw := []byte{79, 170, 47, 0, 0, 6, 'o', 'u', 's', 't', 'e', 'r', 150, 0, 101, 0, 0, 76, 29, 0, 0, 10, 0, 10, 0, 10, 0, 25, 0, 25, 0, 25, 0, 10, 0, 10, 0, 10, 0, 59, 1, 59, 1, 186, 0, 111, 0, 50, 204, 41, 0, 0, 125, 0, 0, 0, 244, 1, 0, 0, 92, 0, 0, 0, 13, 15, 39, 10, 39, 0, 0, 1, 66, 0, 0, 4, 0, 0, 0, 0, 100, 0, 0, 0, 0, 2, 171, 47, 0, 0, 67, 0, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0, 0, 0, 0, 172, 47, 0, 0, 66, 0, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0, 0, 0, 1, 1, 173, 47, 0, 0, 59, 0, 0, 0, 145, 15, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 2, 148, 1, 54, 66, 109, 0, 246, 224, 0, 62, 0, 60, 56, 191, 7, 8, 19, 15, 56, 16, 0, 0, 13, 6, 0, 4, 5, 0, 6, 0, 7, 0, 8, 0, 2, 6, 192, 173, 206, 172, 209, 199, 141, 2, 32, 0, 22, 0, 4, 186, 194, 192, 173, 146, 2, 24, 0, 42, 0, 0, 17, 0, 0, 0, 0, 72, 126, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0} 14 | buf := bytes.NewBuffer(raw) 15 | pkt := &GCUpdateInfoPacket{} 16 | err := pkt.Read(buf, 0) 17 | if err != nil { 18 | t.Errorf("read GCUpdateInfoPacket error:%s\n", err) 19 | } 20 | t.Logf("%#v\n", pkt.PCInfo.(*data.PCOusterInfo)) 21 | 22 | //154, 1, 242, 1, 0, 0, 0, 23 | raw = []byte{83, 221, 118, 0, 0, 4, 203, 171, 208, 222, 1, 1, 125, 1, 224, 1, 0, 76, 29, 0, 0, 13, 0, 13, 0, 13, 0, 14, 0, 14, 0, 14, 0, 20, 0, 20, 0, 20, 0, 1, 204, 41, 0, 0, 248, 4, 0, 0, 221, 5, 0, 0, 211, 15, 0, 0, 54, 0, 54, 0, 50, 0, 50, 0, 0, 0, 0, 0, 244, 1, 0, 0, 0, 50, 0, 0, 0, 0, 50, 0, 0, 0, 0, 40, 0, 0, 0, 7, 102, 3, 0, 0, 0, 30, 0, 0, 0, 0, 64, 66, 15, 0, 13, 114, 73, 57, 0, 0, 0, 72, 215, 1, 99, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 222, 118, 0, 0, 35, 0, 0, 0, 184, 11, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 223, 118, 0, 0, 22, 0, 0, 0, 172, 13, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 224, 118, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 20, 0, 0, 0, 4, 0, 225, 118, 0, 0, 15, 0, 0, 0, 124, 21, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 226, 118, 0, 0, 14, 0, 0, 0, 148, 17, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 4, 3, 227, 118, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0, 0, 9, 4, 228, 118, 0, 0, 1, 5, 0, 0, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 9, 0, 0, 0, 9, 5, 4, 229, 118, 0, 0, 11, 0, 0, 0, 215, 5, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 2, 230, 118, 0, 0, 17, 0, 0, 0, 184, 9, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 3, 230, 118, 0, 0, 17, 0, 0, 0, 184, 9, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 4, 231, 118, 0, 0, 12, 0, 0, 0, 172, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 64, 0, 17, 232, 200, 7, 1, 17, 2, 49, 4, 1, 12, 15, 4, 3, 94, 0, 96, 0, 56, 0, 4, 5, 0, 6, 0, 7, 0, 8, 0, 3, 4, 194, 229, 177, 246, 172, 1, 17, 0, 222, 0, 6, 196, 171, 184, 163, 184, 224, 174, 1, 36, 0, 228, 0, 8, 199, 193, 183, 185, 181, 229, 184, 175, 158, 0, 23, 0, 235, 0, 0, 17, 0, 0, 0, 0, 152, 42, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 52, 1, 5, 0, 0, 0, 1, 0, 221, 118, 0, 0} 24 | buf = bytes.NewBuffer(raw) 25 | pkt = &GCUpdateInfoPacket{} 26 | err = pkt.Read(buf, 0) 27 | if err != nil { 28 | t.Errorf("read GCUpdateInfoPacket error:%s\n", err) 29 | } 30 | t.Logf("%#v\n", pkt.PCInfo.(*data.PCSlayerInfo)) 31 | t.Logf("size=%d\n", pkt.PacketSize()) 32 | // t.Logf("%#v\n", buf.Bytes()) 33 | } 34 | 35 | func TestCGConnectPacket(t *testing.T) { 36 | raw := []byte{0, 230, 91, 4, 0, 4, 203, 171, 208, 222, 8, 0, 39, 203, 215, 129} 37 | // [ 0 0 0 240 1 4 183 232 191 241 0 80 86 192 0 8] 38 | buf := bytes.NewBuffer(raw) 39 | pkt := &CGConnectPacket{} 40 | err := pkt.Read(buf, 0) 41 | if err != nil { 42 | t.Fail() 43 | } 44 | // t.Logf("%#v\n", pkt) 45 | } 46 | 47 | type Dumper interface { 48 | Write(io.Writer, uint8) error 49 | PacketSize() uint32 50 | } 51 | 52 | func TestSize(t *testing.T) { 53 | buf := &bytes.Buffer{} 54 | arr := []Dumper{ 55 | &GCUpdateInfoPacket{ 56 | PCInfo: &data.PCOusterInfo{}, 57 | }, 58 | &GCMoveOKPacket{}, 59 | &GCMoveErrorPacket{}, 60 | &GCMovePacket{}, 61 | &GCPetInfoPacket{}, 62 | &GCSetPositionPacket{}, 63 | &GCAddBat{}, 64 | &GCAddMonsterFromBurrowing{}, 65 | &GCAddMonster{}, 66 | &GCStatusCurrentHP{}, 67 | &GCAttackMeleeOK1{}, 68 | &GCAttackMeleeOK2{}, 69 | &GCAttackMeleeOK3{}, 70 | &GCCannotUsePacket{}, 71 | &GCBloodDrainOK1{}, 72 | &GCModifyInformationPacket{}, 73 | &GCAddEffect{}, 74 | &GCAddMonsterCorpse{}, 75 | &GCCreatureDiedPacket{}, 76 | &GCDeleteObjectPacket{}, 77 | &GCAddEffectPacket{}, 78 | &GCFastMovePacket{}, 79 | &GCLearnSkillOK{}, 80 | &GCRemoveEffect{}, 81 | &GCSkillFailed1Packet{}, 82 | &GCSkillFailed2{}, 83 | &GCSkillToObjectOK1{}, 84 | &GCSkillToObjectOK3{}, 85 | &GCSkillToObjectOK4{}, 86 | &GCSkillToSelfOK1{}, 87 | &GCSkillToSelfOK2{}, 88 | &GCSkillToTileOK1{}, 89 | &GCSkillToTileOK2{}, 90 | &GCSkillToTileOK5{}, 91 | &GCSkillToTileOK4{}, 92 | &GCSkillToTileOK3{}, 93 | &GCSystemMessagePacket{}, 94 | &GCSkillInfoPacket{}, 95 | &GCDisconnect{}, 96 | } 97 | 98 | for _, v := range arr { 99 | buf.Reset() 100 | v.Write(buf, 0) 101 | if uint32(buf.Len()) != v.PacketSize() { 102 | t.Errorf("%s的Size不对:期待%d 实际%d\n", reflect.TypeOf(v), buf.Len(), v.PacketSize()) 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /util/const.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | const ( 4 | MaxViewportWidth = 10 5 | MaxViewportUpperHeight = 14 6 | MaxViewportLowerHeight = 14 7 | MaxVisionWidth = MaxViewportWidth*2 + 1 8 | MaxVisionHeight = MaxViewportUpperHeight + MaxViewportLowerHeight + 1 9 | ) 10 | -------------------------------------------------------------------------------- /util/type.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | type ObjectID_t uint32 4 | type ZoneCoord_t int16 5 | type EffectID_t uint16 6 | type Dir_t uint8 7 | type Range_t uint8 8 | type Sight_t uint8 9 | type Resist_t uint16 10 | 11 | type GuildID_t uint16 12 | type MagicDomain int 13 | 14 | const ( 15 | MAGIC_DOMAIN_NO_DOMAIN MagicDomain = iota 16 | MAGIC_DOMAIN_POISON 17 | MAGIC_DOMAIN_ACID 18 | MAGIC_DOMAIN_CURSE 19 | MAGIC_DOMAIN_BLOOD 20 | MAGIC_DOMAIN_MAX 21 | ) 22 | 23 | type Damage_t uint16 24 | type SpriteType_t uint16 25 | type Luck_t uint16 26 | type Attr_t uint16 27 | type Duration_t uint16 28 | type Sex_t int8 29 | 30 | const ( 31 | FEMALE Sex_t = iota 32 | MALE 33 | ) 34 | 35 | type HairStyle int8 36 | 37 | const ( 38 | HAIR_STYLE1 = iota 39 | HAIR_STYLE2 40 | HAIR_STYLE3 41 | ) 42 | 43 | type Color_t uint16 44 | type Alignment_t int32 45 | type HP_t uint16 46 | type MP_t uint16 47 | type ToHit_t uint16 48 | type Defense_t uint16 49 | type Protection_t uint16 50 | type Speed_t uint8 51 | type Exp_t uint32 52 | type Level_t uint8 53 | type Bonus_t uint16 54 | type SkillBonus_t uint16 55 | type Gold_t uint32 56 | type Fame_t uint32 57 | type SkillType_t uint16 58 | type Silver_t uint16 59 | type Steal_t uint8 60 | type Regen_t uint8 61 | type Elemental_t uint16 62 | type MonsterType_t uint16 63 | 64 | type Vision_t uint8 65 | type SkillPoint_t uint8 66 | 67 | const ( 68 | SKILL_DOMAIN_BLADE = iota 69 | SKILL_DOMAIN_SWORD 70 | SKILL_DOMAIN_GUN 71 | SKILL_DOMAIN_HEAL 72 | SKILL_DOMAIN_ENCHANT 73 | SKILL_DOMAIN_ETC 74 | SKILL_DOMAIN_VAMPIRE 75 | SKILL_DOMAIN_OUSTERS 76 | SKILL_DOMAIN_MAX 77 | ) 78 | 79 | // type Turn_t uint32 80 | type Moral_t uint8 81 | type SkillLevel_t uint8 82 | type ItemID_t uint32 83 | type ExpLevel_t uint16 84 | type ZoneID_t uint16 85 | type ZoneLevel_t uint8 86 | type DarkLevel_t uint8 87 | type LightLevel_t uint8 88 | type NPCType_t uint16 89 | type Coord_t uint8 90 | type Rank_t uint8 91 | type RankExp_t uint32 92 | type GuildMemberRank_t uint8 93 | type SkillExp_t uint32 94 | type ItemType_t uint16 95 | type OptionType_t uint8 96 | type Durability_t uint32 97 | type Grade_t int32 98 | type EnchantLevel_t byte 99 | type SlotID_t uint8 100 | type ItemNum_t uint8 101 | 102 | type CoordInven_t uint8 103 | type NPCID_t uint16 104 | 105 | type ElementalType int8 106 | 107 | const ( 108 | ELEMENTAL_ANY ElementalType = -1 109 | ELEMENTAL_FIRE = iota 110 | ELEMENTAL_WATER 111 | ELEMENTAL_EARTH 112 | ELEMENTAL_WIND 113 | ELEMENTAL_SUM 114 | ELEMENTAL_MAX 115 | ) 116 | 117 | type ZoneType uint8 118 | 119 | const ( 120 | ZONE_NORMAL_FIELD = iota 121 | ZONE_NORMAL_DUNGEON 122 | ZONE_SLAYER_GUILD 123 | ZONE_RESERVED_SLAYER_GUILD 124 | ZONE_PC_VAMPIRE_LAIR 125 | ZONE_NPC_VAMPIRE_LAIR 126 | ZONE_NPC_HOME 127 | ZONE_NPC_SHOP 128 | ZONE_RANDOM_MAP 129 | ZONE_CASTLE 130 | ) 131 | 132 | type ZoneAccessMode uint8 133 | 134 | const ( 135 | ZONE_ACCESS_PUBLIE = iota 136 | ZONE_ACCESS_PRIVATE 137 | ) 138 | 139 | const ( 140 | LEFT = iota 141 | LEFTDOWN 142 | DOWN 143 | RIGHTDOWN 144 | RIGHT 145 | RIGHTUP 146 | UP 147 | LEFTUP 148 | DIR_MAX 149 | DIR_NONE = DIR_MAX 150 | ) 151 | 152 | const ( 153 | NO_SAFE_ZONE = 0x00 154 | SLAYER_SAFE_ZONE = 0x01 155 | VAMPIRE_SAFE_ZONE = 0x02 156 | COMPLETE_SAFE_ZONE = 0x04 157 | NO_PK_ZONE = 0x08 158 | SAFE_ZONE = 0x17 159 | OUSTERS_SAFE_ZONE = 0x10 160 | ) 161 | 162 | type Weather uint8 163 | 164 | const ( 165 | WEATHER_CLEAR Weather = iota 166 | WEATHER_RAINY 167 | WEATHER_SNOWY 168 | WEATHER_MAX 169 | ) 170 | 171 | type MoveMode uint8 172 | 173 | const ( 174 | MOVE_MODE_WALKING MoveMode = iota 175 | MOVE_MODE_FLYING 176 | MOVE_MODE_BURROWING 177 | MOVE_MODE_MAX 178 | ) 179 | 180 | type WeatherLevel_t uint8 181 | 182 | type TPOINT struct { 183 | X int 184 | Y int 185 | } 186 | -------------------------------------------------------------------------------- /util/utils.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "runtime" 5 | "strconv" 6 | ) 7 | 8 | type Rect struct { 9 | X float32 10 | Y float32 11 | W float32 12 | H float32 13 | } 14 | 15 | type FPoint struct { 16 | X float32 17 | Y float32 18 | } 19 | 20 | type Error struct { 21 | e string 22 | file string 23 | line int 24 | } 25 | 26 | func (e *Error) Error() string { 27 | return e.e + "\nat " + e.file + ":" + strconv.Itoa(e.line) 28 | } 29 | 30 | func NewError(str string) *Error { 31 | err := &Error{ 32 | e: str, 33 | } 34 | _, file, line, ok := runtime.Caller(1) 35 | if ok { 36 | err.file = file 37 | err.line = line 38 | } 39 | 40 | return err 41 | } 42 | 43 | func GetPercentValue(value, percent int) int { 44 | return value * percent / 100 45 | } 46 | 47 | func ComputeDirection(originX, originY, destX, destY int) Dir_t { 48 | const ( 49 | BASIS_DIRECTION_HIGH = 2.0 50 | BASIS_DIRECTION_LOW = 0.5 51 | ) 52 | 53 | stepX := destX - originX 54 | stepY := destY - originY 55 | 56 | var k float64 57 | if stepX == 0 { 58 | k = 0 59 | } else { 60 | k = float64(stepY) / float64(stepX) 61 | } 62 | 63 | if stepY == 0 { 64 | switch { 65 | case (stepX == 0): 66 | return DOWN 67 | case stepX > 0: 68 | return RIGHT 69 | default: 70 | return LEFT 71 | } 72 | } else if stepY < 0 { 73 | switch { 74 | case (stepX == 0): 75 | return UP 76 | case (stepX > 0): 77 | switch { 78 | case (k < -BASIS_DIRECTION_HIGH): 79 | return UP 80 | case (k < -BASIS_DIRECTION_LOW): 81 | return RIGHTUP 82 | default: 83 | return RIGHT 84 | } 85 | default: 86 | switch { 87 | case (k > BASIS_DIRECTION_HIGH): 88 | return UP 89 | case (k > BASIS_DIRECTION_LOW): 90 | return LEFTUP 91 | default: 92 | return LEFT 93 | } 94 | } 95 | } else { 96 | switch { 97 | case (stepX == 0): 98 | return DOWN 99 | case (stepX > 0): 100 | switch { 101 | case (k > BASIS_DIRECTION_HIGH): 102 | return DOWN 103 | case (k > BASIS_DIRECTION_LOW): 104 | return RIGHTDOWN 105 | default: 106 | return RIGHT 107 | } 108 | default: 109 | switch { 110 | case (k < -BASIS_DIRECTION_HIGH): 111 | return DOWN 112 | case (k < -BASIS_DIRECTION_LOW): 113 | return LEFTDOWN 114 | default: 115 | return LEFT 116 | } 117 | } 118 | } 119 | return DIR_NONE 120 | } 121 | 122 | func abs(x int) int { 123 | if x > 0 { 124 | return x 125 | } 126 | return -x 127 | } 128 | 129 | func max(x, y int) int { 130 | if x > y { 131 | return x 132 | } 133 | return y 134 | } 135 | 136 | func min(x, y int) int { 137 | if x < y { 138 | return x 139 | } 140 | return y 141 | } 142 | --------------------------------------------------------------------------------