Permalink
Browse files

start kit card game server code.

  • Loading branch information...
0 parents commit d9c63ce10cb8800b3691af99640ece58f3f99a45 @u0u0 u0u0 committed Feb 10, 2014
Showing with 1,284 additions and 0 deletions.
  1. +39 −0 cmCards.go
  2. +118 −0 cmChar.go
  3. +138 −0 cmLogin.go
  4. +230 −0 cmRaid.go
  5. +74 −0 commands.go
  6. +12 −0 data/card/card001.json
  7. +14 −0 data/card/card002.json
  8. +31 −0 data/scene/scene01-01.json
  9. +32 −0 data/scene/scene01-02.json
  10. +32 −0 data/scene/scene02-01.json
  11. +32 −0 data/scene/scene02-02.json
  12. +6 −0 data/skill/skill001.json
  13. +6 −0 data/skill/skill002.json
  14. +16 −0 database.go
  15. +12 −0 errCode.go
  16. +220 −0 gameScript.go
  17. +47 −0 gameServer.go
  18. +29 −0 player.go
  19. +151 −0 readme.md
  20. +45 −0 utils.go
39 cmCards.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "code.google.com/p/go.net/websocket"
+ "github.com/bitly/go-simplejson"
+ "log"
+)
+
+// return all cards of this player, in troop and off troop
+func cmCardsGetHander(this *player, command string, param *simplejson.Json) {
+ rtnCode := 0
+ //TODO make this vaule globle for this player, PUSH_CARDS_ORDER will use it
+ var cardsArray []lvCardData
+
+ defer func() {
+ var rtnMsg interface{}
+ if 0 == rtnCode {
+ rtnMsg = cardsArray
+ } else {
+ rtnMsg, _ = errCodes[rtnCode]
+ }
+ rtnJson := responseJson(command, rtnCode, rtnMsg)
+ log.Println(rtnJson)
+ if err := websocket.Message.Send(this.ws, rtnJson); err != nil {
+ log.Printf("Send fail for cmCardsGetHander")
+ }
+ }()
+
+ //check for charInfo
+ if this.charID == 0 {
+ rtnCode = 6
+ return
+ }
+
+ for _, value := range this.character.cards {
+ card := gGameScript.getLvCard(value.ID, value.Level, value.Status)
+ cardsArray = append(cardsArray, *card)
+ }
+}
118 cmChar.go
@@ -0,0 +1,118 @@
+package main
+
+import (
+ "log"
+ "encoding/json"
+ "code.google.com/p/go.net/websocket"
+ "github.com/bitly/go-simplejson"
+)
+
+func queryCharInfo(this *player) int {
+ rows, err := gamedb.Query(`SELECT cname, clevel, vitality, scene, stage, cards
+ FROM charinfo WHERE cid=?`, this.charID)
+ if err != nil {
+ return 2
+ }
+
+ var cards string
+
+ for rows.Next() {
+ err = rows.Scan(&this.character.CharName, &this.character.Level,
+ &this.character.Vitality,&this.character.Scene,
+ &this.character.Stage, &cards)
+ if err != nil {
+ return 2
+ }
+ // decode cards
+ err = json.Unmarshal([]byte(cards), &this.character.cards)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ return 0
+}
+
+func cmCharCreateHander(this *player, command string, param *simplejson.Json) {
+ rtnCode := 0
+
+ defer func() {
+ var rtnMsg interface{}
+ if 0 == rtnCode {
+ rtnMsg = this.character
+ } else {
+ rtnMsg, _ = errCodes[rtnCode]
+ }
+ rtnJson := responseJson(command, rtnCode, rtnMsg)
+ if err := websocket.Message.Send(this.ws, rtnJson); err != nil {
+ log.Printf("Send fail for cmCharCreateHander")
+ }
+ }()
+
+ //check if login
+ if this.userID == 0 {
+ rtnCode = 6
+ return
+ }
+
+ // get charname from json
+ charname, err := param.Get("CharName").String()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ // insert to table charinfo
+ stmt, err := gamedb.Prepare("INSERT charinfo SET uid=?, cname=?, cards=?")
+ if err != nil {
+ rtnCode = 2
+ return
+ }
+
+ // TODO: read default vaule from config
+ cards := `[{"ID":1, "Level":1, "Status":1},{"ID":2, "Level":1, "Status":1}]`//json array
+
+ res, err := stmt.Exec(this.userID, charname, cards)
+ if err != nil {
+ log.Print(err)
+ rtnCode = 5
+ return
+ }
+
+ cid, err := res.LastInsertId()
+ if err != nil {
+ rtnCode = 2
+ log.Printf("Error: database not support LastInsertId()")
+ return
+ }
+
+ // safe cast for mysql INT
+ this.charID = uint32(cid)
+ // query that we jsut insert
+ rtnCode = queryCharInfo(this)
+}
+
+func cmCharGetHander(this *player, command string, param *simplejson.Json) {
+ rtnCode := 0
+
+ defer func() {
+ var rtnMsg interface{}
+ if 0 == rtnCode {
+ rtnMsg = this.character
+ } else {
+ rtnMsg, _ = errCodes[rtnCode]
+ }
+ rtnJson := responseJson(command, rtnCode, rtnMsg)
+ if err := websocket.Message.Send(this.ws, rtnJson); err != nil {
+ log.Printf("Send fail for cmCharGetHander")
+ }
+ }()
+
+ //check if login
+ if this.userID == 0 || this.charID == 0{
+ rtnCode = 6
+ return
+ }
+
+ rtnCode = queryCharInfo(this)
+}
138 cmLogin.go
@@ -0,0 +1,138 @@
+package main
+
+import (
+ "log"
+ "code.google.com/p/go.net/websocket"
+ "github.com/bitly/go-simplejson"
+)
+
+func cmRegisterHander(this *player, command string, param *simplejson.Json) {
+ rtnCode := 0
+
+ defer func() {
+ var rtnMsg interface{}
+ if 0 == rtnCode {
+ // client need display create character UI
+ rtnMsg = "CreateCharacter"
+ } else {
+ rtnMsg, _ = errCodes[rtnCode]
+ }
+ rtnJson := responseJson(command, rtnCode, rtnMsg)
+ if err := websocket.Message.Send(this.ws, rtnJson); err != nil {
+ log.Printf("Send fail for cmRegisterHander")
+ }
+ }()
+
+ // parse json
+ username, err := param.Get("Username").String()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ passwd, err := param.Get("Password").String()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ email, err := param.Get("Email").String()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ // insert to db
+ stmt, err := gamedb.Prepare("INSERT userinfo SET username=?, passwd=?, email=?, registered=?")
+ if err != nil {
+ rtnCode = 2
+ return
+ }
+
+ res, err := stmt.Exec(username, sumSha1(passwd), email, nowToDateTime())
+ if err != nil {
+ rtnCode = 3
+ return
+ }
+
+ uid, err := res.LastInsertId()
+ if err != nil {
+ rtnCode = 2
+ log.Printf("Error: database not support LastInsertId()")
+ return
+ }
+
+ // safe cast for mysql INT
+ this.userID = uint32(uid)
+}
+
+func cmLoginHander(this *player, command string, param *simplejson.Json) {
+ rtnCode := 0
+
+ defer func() {
+ var rtnMsg interface{}
+ if 0 == rtnCode && 0 == this.charID {
+ // client need display create character UI
+ rtnMsg = "CreateCharacter"
+ } else {
+ rtnMsg, _ = errCodes[rtnCode]
+ }
+ rtnJson := responseJson(command, rtnCode, rtnMsg)
+ if err := websocket.Message.Send(this.ws, rtnJson); err != nil {
+ log.Printf("Send fail for cmLoginHander")
+ }
+ }()
+
+ // parse json
+ username, err := param.Get("Username").String()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ passwd, err := param.Get("Password").String()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ // query password of the user
+ rows, err := gamedb.Query("SELECT uid,passwd FROM userinfo WHERE username=?", username)
+ if err != nil {
+ rtnCode = 2
+ return
+ }
+
+ var uid uint32
+ var dbPasswd string
+ for rows.Next() {
+ if err := rows.Scan(&uid, &dbPasswd); err != nil {
+ rtnCode = 2
+ return
+ }
+ }
+
+ if sumSha1(passwd) != dbPasswd {
+ rtnCode = 4
+ return
+ }
+
+ this.userID = uid
+
+ // check if have game character
+ rows, err = gamedb.Query("SELECT cid FROM charinfo WHERE uid=?", uid)
+ if err != nil {
+ rtnCode = 2
+ return
+ }
+
+ var cid uint32
+ for rows.Next() {
+ if err := rows.Scan(&cid); err != nil {
+ rtnCode = 2
+ return
+ }
+ }
+
+ this.charID = cid
+}
230 cmRaid.go
@@ -0,0 +1,230 @@
+package main
+
+import (
+ "log"
+ "code.google.com/p/go.net/websocket"
+ "github.com/bitly/go-simplejson"
+)
+
+type raidCombatData struct {
+ Attacker string
+ Beattacked string
+ Skill int
+ Damage int
+}
+
+type raidEndData struct {
+ Result int
+ Drop []int
+ Experience int
+}
+
+type raidResponseData struct {
+ My []combatCardData
+ Monster []combatCardData
+ Combat []raidCombatData
+ End raidEndData
+}
+
+func cmRaidHander(this *player, command string, param *simplejson.Json) {
+ rtnCode := 0
+ var raidResponse raidResponseData
+
+ defer func() {
+ var rtnMsg interface{}
+ if 0 == rtnCode {
+ rtnMsg = raidResponse
+ } else {
+ rtnMsg, _ = errCodes[rtnCode]
+ }
+ rtnJson := responseJson(command, rtnCode, rtnMsg)
+ if err := websocket.Message.Send(this.ws, rtnJson); err != nil {
+ log.Printf("Send fail for cmRaidHander")
+ }
+ }()
+
+ //check for charInfo
+ if this.charID == 0 {
+ rtnCode = 6
+ return
+ }
+
+ scene, err := param.Get("Scene").Int()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ stage, err := param.Get("Stage").Int()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ // prepare data
+ var myCards []combatCardData
+ for index, value := range this.character.cards {
+ card := gGameScript.getCombatCard(value.ID, value.Level, value.Status, index + 1)
+ myCards = append(myCards, *card)
+ }
+
+ stageInfo := gGameScript.getStage(scene, stage)
+ if stageInfo == nil {
+ rtnCode = 7
+ return
+ }
+
+ var monsterCards []combatCardData
+ for _, value := range stageInfo.Monster {
+ card := gGameScript.getCombatCard(value.ID, value.Level, 0, value.Position)
+ monsterCards = append(monsterCards, *card)
+ }
+
+ // copy to keep HP full in json
+ raidResponse.My = make([]combatCardData, len(myCards))
+ copy(raidResponse.My, myCards)
+ raidResponse.Monster = make([]combatCardData, len(monsterCards))
+ copy(raidResponse.Monster, monsterCards)
+
+ curMy := 1
+ aliveMy := len(myCards)
+ curMonster := 1
+ aliveMonster := len(monsterCards)
+ turn := true // true for my, false for monster
+ for {
+ //check end
+ if aliveMy == 0 || aliveMonster == 0 {
+ break
+ }
+
+ var attArr []combatCardData
+ var beAttArr []combatCardData
+ var curAttacker *int
+ var alive *int
+
+ if turn {
+ attArr = myCards
+ beAttArr = monsterCards
+ curAttacker = &curMy
+ alive = &aliveMonster
+ } else {
+ attArr = monsterCards
+ beAttArr = myCards
+ curAttacker = &curMonster
+ alive = &aliveMy
+ }
+ // change turn
+ turn = !turn
+
+ // get attacker
+ attacker := getLiveCard(attArr, curAttacker, len(attArr))
+ // point to next attacker
+ *curAttacker++
+ if *curAttacker > len(attArr) {
+ *curAttacker = 1
+ }
+
+ // choose skill
+ // normal attack + special skill
+ totolSkill := 1 + len(attacker.Skill)
+ skillIndex := randInRange(0, totolSkill)
+
+ attackRange := 1 //normal attack range
+ if skillIndex > 0 {
+ // special skill atack range
+ attackRange = gGameScript.getSkill(skillIndex).Range
+ }
+
+ var attackPos int
+ if attacker.Pos < 3 { // attacker in first line
+ if attackRange < 2 {
+ attackPos = attacker.Pos
+ } else {
+ attackPos = attacker.Pos + 2
+ }
+ } else { // attack in second line
+ if attackRange < 2 {
+ attackPos = 1
+ } else {
+ attackPos = attacker.Pos
+ }
+ }
+ // XXX:Fix attackPos by max card counts
+ if attackPos > len(beAttArr) {
+ attackPos = len(beAttArr)
+ }
+
+ // get BeAttacked
+ beAttacked := getLiveCard(beAttArr, &attackPos, len(beAttArr))
+
+ // count hurtPoint
+ attackPower := 1.0
+ if skillIndex > 1 {
+ attackPower = float64(gGameScript.getSkill(skillIndex).Power) / 100.0
+ }
+ hurtPoint := int((float64(attacker.Level) * 2 / 5) * float64(attacker.Attack) * attackPower / (float64(beAttacked.Defence) / 10))
+
+ beAttacked.HP -= hurtPoint
+ if beAttacked.HP <= 0 {
+ *alive--
+ }
+
+ cbInfo := raidCombatData{attacker.Hash, beAttacked.Hash, skillIndex, hurtPoint}
+ raidResponse.Combat = append(raidResponse.Combat, cbInfo)
+ }
+
+ // the end info
+ raidResponse.End.Experience = 0
+ if aliveMy > 0 {
+ raidResponse.End.Result = 1
+ // count exp
+ for _, card := range monsterCards {
+ raidResponse.End.Experience += (card.Experience * card.Level) / (7 * len(myCards))
+ }
+ // drop
+ for _, award := range stageInfo.Award {
+ card := gGameScript.getCard(award)
+ rand := randInRange(0, 100)
+ if rand < card.Drop {
+ raidResponse.End.Drop = append(raidResponse.End.Drop, award)
+ }
+ }
+ // TODO save progress
+ // TODO add drop to database
+ } else {
+ raidResponse.End.Result = 0
+ }
+}
+
+// pos [1, 5]
+func getCardOfPos(cards []combatCardData, pos int) *combatCardData {
+ for index := 0; index < len(cards); index++ {
+ if cards[index].Pos == pos {
+ return &cards[index]
+ }
+ }
+ log.Println("Error:getCardOfPos fail")
+ return nil
+}
+
+// maxPos [1, 5]
+func getLiveCard(cards []combatCardData, startPos *int, maxPos int) *combatCardData {
+ pos := *startPos
+ for {
+ card := getCardOfPos(cards, pos)
+ if card.HP > 0 {
+ *startPos = pos
+ return card
+ }
+ pos++
+ if pos > maxPos {
+ pos = 1
+ }
+
+ if pos == *startPos {
+ log.Println("Error:getLiveCard fail")
+ break
+ }
+ }
+ return nil
+}
74 commands.go
@@ -0,0 +1,74 @@
+package main
+
+import (
+ "log"
+ "code.google.com/p/go.net/websocket"
+ "github.com/bitly/go-simplejson"
+)
+
+type commandHandler func(this *player, command string, param *simplejson.Json)
+
+type jsonReturn struct {
+ Code int
+ Message interface{}
+}
+
+type jsonResponse struct {
+ Command string
+ Return jsonReturn
+}
+
+func responseJson(command string, code int, message interface{}) string {
+ msg := jsonResponse{
+ Command : command,
+ Return : jsonReturn{
+ Code : code,
+ Message : message,
+ },
+ }
+ return makeJson(msg)
+}
+
+// handler map for "Command"
+var cmHandlers = map[string]commandHandler{
+ "CM_REGISTER" : cmRegisterHander,
+ "CM_LOGIN" : cmLoginHander,
+ "CM_CHAR_CREATE" : cmCharCreateHander,
+ "CM_CHAR_GET" : cmCharGetHander,
+ "CM_CARDS_GET" : cmCardsGetHander,
+ "CM_RAID" : cmRaidHander,
+}
+
+func commandDispatcher(this *player, js *simplejson.Json) {
+ rtnCode := 0
+ command := ""
+
+ // defer need to be placed at header of func
+ defer func() {
+ // only send error message here
+ if 0 != rtnCode {
+ rtnMsg, _ := errCodes[rtnCode]
+ rtnJson := responseJson(command, rtnCode, rtnMsg)
+ if err := websocket.Message.Send(this.ws, rtnJson); err != nil {
+ log.Printf("Send fail for commandDispatcher")
+ }
+ }
+ }()
+
+ command, err := js.Get("Command").String()
+ if err != nil {
+ rtnCode = 1
+ return
+ }
+
+ param, ok := js.CheckGet("Param")
+ if ok {
+ handler, ok := cmHandlers[command]
+ if ok {
+ handler(this, command, param)
+ return
+ }
+ }
+
+ rtnCode = 1
+}
12 data/card/card001.json
@@ -0,0 +1,12 @@
+{
+ "Name": "田园犬",
+ "HP": 120,
+ "Attack": 120,
+ "Defence": 120,
+ "Speed": 120,
+ "Talent": 720,
+ "Drop": 50,
+ "Experience": 324,
+ "Skill": [
+ ]
+}
14 data/card/card002.json
@@ -0,0 +1,14 @@
+{
+ "Name": "小花猫",
+ "HP": 120,
+ "Attack": 300,
+ "Defence": 120,
+ "Speed": 120,
+ "Talent": 720,
+ "Drop": 50,
+ "Experience": 324,
+ "Skill": [
+ 1,
+ 2
+ ]
+}
31 data/scene/scene01-01.json
@@ -0,0 +1,31 @@
+{
+ "Intro": "{gamename}欢迎你来到 {username} 给{gamename}留言,交流看法",
+ "Dialog": [
+ {
+ "Name": "姓名",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ },
+ {
+ "Name": "{username}",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ }
+ ],
+ "Monster": [
+ {
+ "ID": 1,
+ "Level": 1,
+ "Position": 1
+ },
+ {
+ "ID": 1,
+ "Level": 1,
+ "Position": 2
+ }
+ ],
+ "Award": [
+ 1
+ ],
+ "Vitality": 2
+}
32 data/scene/scene01-02.json
@@ -0,0 +1,32 @@
+{
+ "Intro": "{gamename}欢迎你来到 {username} 给{gamename}留言,交流看法",
+ "Dialog": [
+ {
+ "Name": "是我",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ },
+ {
+ "Name": "{username}",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ }
+ ],
+ "Monster": [
+ {
+ "ID": 1,
+ "Level": 1,
+ "Position": 1
+ },
+ {
+ "ID": 2,
+ "Level": 1,
+ "Position": 2
+ }
+ ],
+ "Award": [
+ 1,
+ 2
+ ],
+ "Vitality": 2
+}
32 data/scene/scene02-01.json
@@ -0,0 +1,32 @@
+{
+ "Intro": "{gamename}欢迎你来到 {username} 给{gamename}留言,交流看法",
+ "Dialog": [
+ {
+ "Name": "姓名",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ },
+ {
+ "Name": "{username}",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ }
+ ],
+ "Monster": [
+ {
+ "ID": 1,
+ "Level": 1,
+ "Position": 1
+ },
+ {
+ "ID": 2,
+ "Level": 1,
+ "Position": 2
+ }
+ ],
+ "Award": [
+ 1,
+ 2
+ ],
+ "Vitality": 2
+}
32 data/scene/scene02-02.json
@@ -0,0 +1,32 @@
+{
+ "Intro": "{gamename}欢迎你来到 {username} 给{gamename}留言,交流看法",
+ "Dialog": [
+ {
+ "Name": "是我",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ },
+ {
+ "Name": "{username}",
+ "Pic": "图片地址",
+ "Talk": "{username} 欢迎进入{gamename}!"
+ }
+ ],
+ "Monster": [
+ {
+ "ID": 1,
+ "Level": 1,
+ "Position": 1
+ },
+ {
+ "ID": 2,
+ "Level": 1,
+ "Position": 2
+ }
+ ],
+ "Award": [
+ 1,
+ 2
+ ],
+ "Vitality": 2
+}
6 data/skill/skill001.json
@@ -0,0 +1,6 @@
+{
+ "Name": "狂爪",
+ "Power": 150,
+ "Hit": 90,
+ "Range": 1
+}
6 data/skill/skill002.json
@@ -0,0 +1,6 @@
+{
+ "Name": "狂怒",
+ "Power": 150,
+ "Hit": 100,
+ "Range": 1
+}
16 database.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "database/sql"
+ _ "github.com/go-sql-driver/mysql"
+)
+
+// TODO: load db info from config file
+var gamedb *sql.DB
+func initDB() {
+ var err error
+ gamedb, err = sql.Open("mysql", "kapai:kapai123@/kapai?charset=utf8")
+ if err != nil {
+ panic(err)
+ }
+}
12 errCode.go
@@ -0,0 +1,12 @@
+package main
+
+var errCodes = map[int]string{
+ 0 : "Success",
+ 1 : "Json parse fail", // fail for parse json of client
+ 2 : "general database fail", // server fail for sql exec
+ 3 : "register fail:duplicate name",
+ 4 : "login fail: wrong password",
+ 5 : "create char fail: duplicate name",
+ 6 : "require login",
+ 7 : "stage out of range",
+}
220 gameScript.go
@@ -0,0 +1,220 @@
+package main
+
+import (
+ "io/ioutil"
+ "encoding/json"
+ "fmt"
+ "log"
+)
+
+//FIXME: change to your own data path.
+//make sure end with '/'
+const gDataPath = "/Users/u0u0/mygo/src/gameServer/"
+
+// scene.json
+type monsterData struct {
+ ID int
+ Level int
+ Position int
+}
+
+type stageDate struct {
+ Monster []monsterData
+ Award []int
+ Vitality int
+}
+// end of scene.json
+
+// stages of a scene
+type sceneData struct {
+ stages []stageDate
+}
+
+// card.json
+// base info
+type cardData struct {
+ Name string
+ HP int
+ Attack int
+ Defence int
+ Speed int
+ Talent int
+ Drop int
+ Experience int
+ Skill []int
+}
+
+// card info of level addition
+type lvCardData struct {
+ // anonymous filed
+ cardData
+ // additional info
+ ID string
+ Level int
+ Status int // 1: in troop, 0: off troop
+ Hash string
+}
+
+// combat card info
+type combatCardData struct {
+ lvCardData
+ Pos int
+}
+
+// skill.json
+type skillData struct {
+ Name string
+ Power int
+ Hit int
+ Range int
+}
+
+// all scene, card, skill data info
+type gameScript struct {
+ scenes []sceneData
+ cards []cardData
+ skills []skillData
+}
+
+// ======global var define
+var gGameScript gameScript
+
+// =========== method of gameScript =========
+func (script *gameScript) getLvCard(id int, level int, status int) *lvCardData {
+ idString := fmt.Sprintf("%03d", id)
+ lvData := lvCardData{cardData:*script.getCard(id), ID:idString, Level:level, Status:status}
+ lvData.Hash = fmt.Sprintf("%p", &lvData)// %p to print point address
+ //corretive HP, Attack, Defence
+ lvData.HP = (lvData.Talent * 2 + lvData.HP) * level / 100 + 5 + level
+ lvData.Attack = (lvData.Talent * 2 + lvData.Attack) * level / 100 + 10
+ lvData.Defence = (lvData.Defence * 2 + lvData.Defence) * level / 100 + 10
+ return &lvData
+}
+
+func (script *gameScript) getCombatCard(id int, level int, status int, pos int) *combatCardData {
+ return &combatCardData{*script.getLvCard(id, level, status), pos}
+}
+
+// id [1,n]
+func (script *gameScript) getCard(id int) *cardData {
+ if id < 1 || id > len(script.cards) {
+ log.Println("Error: getCard out of range", id)
+ return nil
+ }
+ return &script.cards[id - 1]
+}
+
+// scene [1,n]
+// stage [1,n]
+func (script *gameScript) getStage(scene int, stage int) *stageDate {
+ if scene < 1 || scene > len(script.scenes) {
+ log.Println("Error: scene out of range", scene)
+ return nil
+ }
+ if stage < 1 || stage > len(script.scenes[scene - 1].stages) {
+ log.Println("Error: stage out of range", stage)
+ return nil
+ }
+ return &script.scenes[scene - 1].stages[stage - 1]
+}
+
+// id [1,n]
+func (script *gameScript) getSkill(id int) *skillData {
+ if id < 1 || id > len(script.skills) {
+ log.Println("Error: getSkill out of range", id)
+ return nil
+ }
+ return &script.skills[id - 1]
+}
+
+func (script *gameScript) initSceneData() {
+ totalScene := 0
+ for {
+ filepath := fmt.Sprintf("%sdata/scene/scene%02d-01.json", gDataPath, totalScene + 1)
+ if false == isExist(filepath) {
+ break
+ }
+ totalScene++
+
+ stageOfScene := 0
+ var scene sceneData
+ for {
+ filepath = fmt.Sprintf("%sdata/scene/scene%02d-%02d.json",
+ gDataPath, totalScene, stageOfScene + 1)
+ if false == isExist(filepath) {
+ break
+ }
+ // read whole file to buffer
+ buf, err := ioutil.ReadFile(filepath)
+ if err != nil {
+ panic(err)
+ }
+
+ stageOfScene++
+
+ var stage stageDate
+ err = json.Unmarshal(buf, &stage)
+ if err != nil {
+ panic(err)
+ }
+ scene.stages = append(scene.stages, stage)
+ }
+ // important: add scene after the scene changed
+ script.scenes = append(script.scenes, scene)
+ }
+}
+
+func (script *gameScript) initCardData() {
+ totalCard := 0
+ for {
+ filepath := fmt.Sprintf("%sdata/card/card%03d.json", gDataPath, totalCard + 1)
+ if false == isExist(filepath) {
+ break
+ }
+ // read whole file to buffer
+ buf, err := ioutil.ReadFile(filepath)
+ if err != nil {
+ panic(err)
+ }
+
+ totalCard++
+
+ var card cardData
+ err = json.Unmarshal(buf, &card)
+ if err != nil {
+ panic(err)
+ }
+ script.cards = append(script.cards, card)
+ }
+}
+
+func (script *gameScript) initSkillData() {
+ totalSkill := 0
+ for {
+ filepath := fmt.Sprintf("%sdata/skill/skill%03d.json", gDataPath, totalSkill + 1)
+ if false == isExist(filepath) {
+ break
+ }
+ // read whole file to buffer
+ buf, err := ioutil.ReadFile(filepath)
+ if err != nil {
+ panic(err)
+ }
+
+ totalSkill++
+
+ var skill skillData
+ err = json.Unmarshal(buf, &skill)
+ if err != nil {
+ panic(err)
+ }
+ script.skills = append(script.skills, skill)
+ }
+}
+
+// script module init
+func initGameData() {
+ gGameScript.initSceneData()
+ gGameScript.initCardData()
+ gGameScript.initSkillData()
+}
47 gameServer.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "log"
+ "net/http"
+ "code.google.com/p/go.net/websocket"
+ "github.com/bitly/go-simplejson"
+)
+
+func wsHandler(ws *websocket.Conn) {
+ var err error
+ var this player // link ws with player
+
+ this.ws = ws
+ // need loop to keep socket connect
+ for {
+ var reply string
+
+ if err = websocket.Message.Receive(ws, &reply); err != nil {
+ log.Printf("connect closed!")
+ break
+ }
+
+ js, err := simplejson.NewJson([]byte(reply))
+ if err != nil {
+ // TODO: Send error json back to client
+ log.Printf("parse json error:", err);
+ continue
+ }
+
+ commandDispatcher(&this, js)
+ }
+}
+
+func main() {
+ log.Print("initing database ...");
+ initDB()
+ log.Print("initing game script data ...");
+ initGameData()
+
+ log.Print("starting socket server ...");
+ //TODO read port from config
+ http.Handle("/", websocket.Handler(wsHandler))
+ if err := http.ListenAndServe(":1234", nil); err != nil {
+ log.Fatal("ListenAndServe:", err)
+ }
+}
29 player.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "code.google.com/p/go.net/websocket"
+)
+
+type cardsOfChar struct {
+ Status int
+ ID int
+ Level int
+}
+
+// charinfo table data
+type charInfo struct {
+ CharName string
+ Level int
+ Vitality int
+ Scene int
+ Stage int
+ cards []cardsOfChar
+}
+
+type player struct {
+ ws *websocket.Conn // ws of this player
+ userID uint32 // uid of table userinfo
+ charID uint32 // cid of table character
+
+ character charInfo
+}
151 readme.md
@@ -0,0 +1,151 @@
+# game server use golang and websocket
+
+## Login module
+Register
+```
+{
+ "Command" : "CM_REGISTER",
+ "Param":
+ {
+ "Username" : "xxx",
+ "Password" : "aa",
+ "Email" : "a@x.com"
+ }
+}
+
+{
+ "Command" : "CM_REGISTER",
+ "Return" :
+ {
+ "Code" : 0,
+ "Message" : "CreateCharacter"
+ }
+}
+```
+
+Login
+```
+{
+ "Command" : "CM_LOGIN",
+ "Param" :
+ {
+ "Username" : "xxx",
+ "Password" : "aa"
+ }
+}
+
+{
+ "Command" : "CM_LOGIN",
+ "Return" :
+ {
+ "Code" : 0,
+ "Message" : "Success" // or "CreateCharacter"
+ }
+}
+```
+
+Create character (LOGIN FIRST)
+```
+{
+ "Command" : "CM_CHAR_CREATE",
+ "Param" :
+ {
+ "CharName" : "xxx",
+ }
+}
+
+{
+ "Command" : "CM_CHAR_CREATE",
+ "Return" :
+ {
+ "Code" : 0,
+ "Message" : {json of char info}
+ }
+}
+```
+
+Get character (LOGIN FIRST)
+```
+{
+ "Command" : "CM_CHAR_GET",
+ "Param" : ""
+}
+
+{
+ "Command" : "CM_CHAR_GET",
+ "Return" :
+ {
+ "Code" : 0,
+ "Message" : {json of char info}
+ }
+}
+```
+
+json of char info
+```
+{
+ "CharName": "a New Name ",
+ "Level" : 1,
+ "Vitality": 60,
+ "Scene": 1,
+ "Stage" : 1
+}
+```
+
+Get card array
+```
+{
+ "Command" : "CM_CARDS_GET",
+ "Param" : ""
+}
+
+{
+ "Command" : "CM_CARDS_GET",
+ "Return" :
+ {
+ "Code" : 0,
+ "Message" : {json of card array}
+ }
+}
+```
+
+json of card array
+```
+[
+{
+ "Name": "小花猫",
+ "HP": 21,
+ "Attack": 27,
+ "Defence": 13,
+ "Speed": 120,
+ "Talent": 720,
+ "Drop": 50,
+ "Experience": 324,
+ "Skill": [1,2],
+ "ID": "002",
+ "Level": 1,
+ "Status": 1,
+ "Hash": "0xc2000e9630",
+}
+]
+```
+
+Raid
+```
+{
+ "Command" : "CM_RAID",
+ "Param" : {
+ "Scene" : 1,
+ "Stage" : 1
+ }
+}
+
+{
+ "Command" : "CM_RAID",
+ "Return" :
+ {
+ "Code" : 0,
+ "Message" : "{battle command order}"
+ }
+}
+```
45 utils.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "io"
+ "time"
+ "math/rand"
+ "fmt"
+ "crypto/sha1"
+ "encoding/json"
+ "log"
+ "os"
+)
+
+func sumSha1(str string) string {
+ t := sha1.New();
+ io.WriteString(t, str);
+ return fmt.Sprintf("%x",t.Sum(nil));
+}
+
+// now to sql DATETIME string
+// refer to http://ichon.me/post/998.html
+func nowToDateTime() string {
+ return time.Now().Format("2006-01-02 15:04:05")
+}
+
+func makeJson(v interface{}) string {
+ bin, err := json.Marshal(v)
+ if err != nil {
+ log.Print(err)
+ return "Marshal json fail!"
+ }
+ return string(bin)
+}
+
+func isExist(filename string) bool {
+ _, err := os.Stat(filename)
+ return err == nil || os.IsExist(err)
+}
+
+// return [start, end)
+func randInRange(start int, end int) int {
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+ num := r.Intn(end)
+ return num + start
+}

0 comments on commit d9c63ce

Please sign in to comment.