yokai_server
yokai_server is a game server with horizontally-scalable and high-available. It was powered by go-micro and running in docker container.
Requirement
- MongoDB
- Redis-json module
Getting Started
docker-compose run --service-ports -d mongo
- Start Redis with json module - running in
docker-compose
:
docker-compose run --service-ports -d rejson
- Start Gate - cd to
apps/gate
, run following command:
go run main.go
- Start Game - open another terminal session, cd to
apps/game
, run following command:
go run main.go
- Start Combat - open another terminal session, cd to
apps/combat
, run following command:
go run main.go
- Start Client - open another terminal session, cd to
apps/client
, run following command:
go run main.go
now you can communicate with server using (up down left right enter):
Using store to save object in cache and database
- first add a new store info
func init() {
// add store info
store.GetStore().AddStoreInfo(define.StoreType_Account, "account", "_id", "")
store.GetStore().AddStoreInfo(define.StoreType_Player, "player", "_id", "")
store.GetStore().AddStoreInfo(define.StoreType_Item, "item", "_id", "owner_id")
store.GetStore().AddStoreInfo(define.StoreType_Hero, "hero", "_id", "owner_id")
store.GetStore().AddStoreInfo(define.StoreType_Rune, "rune", "_id", "owner_id")
store.GetStore().AddStoreInfo(define.StoreType_Token, "token", "_id", "owner_id")
}
- load single object example
func (m *TokenManager) LoadAll() {
err := store.GetStore().LoadObject(define.StoreType_Token, m.owner.GetID(), m)
if err != nil {
store.GetStore().SaveObject(define.StoreType_Token, m)
return
}
}
func (m *HeroManager) LoadAll() {
heroList, err := store.GetStore().LoadArray(define.StoreType_Hero, m.owner.GetID(), hero.GetHeroPool())
if err != nil {
logger.Error("load hero manager failed:", err)
}
for _, i := range heroList {
err := m.initLoadedHero(i.(hero.Hero))
if err != nil {
logger.Error("load hero failed:", err)
}
}
}
func (m *HeroManager) createEntryHero(entry *define.HeroEntry) hero.Hero {
/*
if entry == nil {
logger.Error("newEntryHero with nil HeroEntry")
return nil
}
id, err := utils.NextID(define.SnowFlake_Hero)
if err != nil {
logger.Error(err)
return nil
}
*/
h := hero.NewHero(
hero.Id(id),
hero.OwnerId(m.owner.GetID()),
hero.OwnerType(m.owner.GetType()),
hero.Entry(entry),
hero.TypeId(entry.ID),
)
//h.GetAttManager().SetBaseAttId(entry.AttID)
//m.mapHero[h.GetOptions().Id] = h
store.GetStore().SaveObject(define.StoreType_Hero, h)
//h.GetAttManager().CalcAtt()
return h
}
- save several fields example
func (m *TokenManager) save() error {
fields := map[string]interface{}{
"tokens": m.Tokens,
"serialize_tokens": m.SerializeTokens,
}
store.GetStore().SaveFields(define.StoreType_Token, m, fields)
return nil
}
- full save and load example useing
lru cache
and sync.Pool
func (am *AccountManager) GetLitePlayer(playerId int64) (player.LitePlayer, error) {
am.RLock()
defer am.RUnlock()
if lp, ok := am.litePlayerCache.Get(playerId); ok {
return *(lp.(*player.LitePlayer)), nil
}
lp := am.litePlayerPool.Get().(*player.LitePlayer)
err := store.GetStore().LoadObject(define.StoreType_LitePlayer, playerId, lp)
if err == nil {
am.litePlayerCache.Add(lp.ID, lp)
return *lp, nil
}
am.litePlayerPool.Put(lp)
return *(player.NewLitePlayer().(*player.LitePlayer)), err
}
License
yokai_server is MIT licensed.