Skip to content

Instantly share code, notes, and snippets.

@umegaya
Last active December 13, 2015 20:18
Show Gist options
  • Save umegaya/4969064 to your computer and use it in GitHub Desktop.
Save umegaya/4969064 to your computer and use it in GitHub Desktop.
ゾーンとユーザーのスケーリングについての設計
mrogueのクラスライブラリとintegrateさせて実装する
Klass:find(id)でKlassのインスタンスを検索できるが、通常これはfindを呼び出したスレッドで生成されたオブジェクトだけがみつかる。
これをほかのスレッドのオブジェクトも見つかるように拡張する。見つかるようにするかどうかはオプショナルに設定できる。
見つかるようにしたオブジェクトをuniversalである、と呼ぶようにしよう
Object > UniversalObject としてそれを継承させる
Object.map[Klass]に設定されるのは通常はただのテーブル(ゆえに同じスレッドからしか見つからない)
ここにyue側のmapを使えるようにして、thread emitterのポインタを設定するのがよかろうか
yue.map = function (size)
setmetatable({
__gmap = yue.rawmap(size) --> process object lookup
}, {
__index = function (t, k)
local ptr = t.__gmap:find(k)
local em = ptr and yue.thread(ptr) or yue.socket(yue.conhash(k))
return setmetatble({ __procs = emitter.procs }, {
__index = function (t, k)
t[k] = function (self, ...)
t.__procs.m(self._id, k, ...) --> this is rpc 'm'
end
return t[k]
end,
})
end,
__newindex = function (t, k, v)
if v then
if not t.__gmap:alloc(k, v._owner.__ptr) then
error('cannot insert object for ' .. k)
end
else
t.__gmap:erase(k)
end
t[k] = v
end,
})
end
class.UniversalObject.extends.Object {
DEFAULT_MAPSIZE = 100,
map = {},
universal_initialize = function (self)
local map = class.UniversalObject.map
if not map[self.class.name] then
map[self.class.name] = yue.map(self.class.mapsize or class.UniversalObject.DEFAULT_MAPSIZE)
end
self._id = yue.uuid()
local addr = yue.conhash(self._id)
if addr == yue.localaddr then
self._owner = yue.thread.current
map[self._id] = self
else
local c = yue.open(addr)
c.c(self._id, self.class.name, ...) --> because this object should not be assigned to this node, create it on remote
self._owner = false
end
end,
destroy = function (self)
local map = class.UniversalObject.map
map[self._id] = nil
self._owner = nil
end,
owner = function (self)
return self._owner
end,
}
UniversalObjectのインスタンスに対するメソッド呼び出しはそのスレッドがownerであれば直接呼び出し、そうでなければRPCになる
_ownerをもしsocketにできればリモートに対しても同じことができるが、すべてのオブジェクトを直接テーブルに格納するのは(全ノードが保持しているオブジェクトのキーを全ノードで共有することとなり)不可能なので、
idからconsistent hash的な方法でそのオブジェクトが存在するノードを調べて適当にRPCするというのが良さそう
ローカルの検索で見つからなかった場合にそうするようにする
その場合、オブジェクトを作ろうとしたノードが自分でオブジェクトが作れないこともある。担当するノードにRPCしてオブジェクトを作成する
そのノードにおけるオブジェクトの作成は中断されるべき。(自ノードで作る場合にのみinitialize以降が呼び出される)
耐障害性を検討するのであれば、同時にレプリカも作成されることになるか。yue.conhashが複数アドレスを返す感じにするか、作成先で複製数が規定の数にみたない場合は、さらにレプリケーション先をconhashから探してコピーするか。
フロアあたりのデータ量がどのくらいになるかにもよるけど。
レプリカへのデータ転送はできれば変更された部分だけを送りたいがうまくいくかしら
オブジェクトごとに__packみたいなので変更分をパックできるようにする、つまりアプリケーション依存にした方がよかろうか
=>ゾーンに関しては、完全なスナップショットはすくなくともゲームにおいてはいらない気がする。存在しているオブジェクトの数とパラメータがあっていればいい
=>何秒かに一回とか、そんなんでもいいんじゃなかろうか。
=>ユーザーデータだけは執拗に複製して、アイテムなくなったぞ的なことができるだけおこらないようにする
それぞれのUniversalObjectに対して、レプリカの何かを呼び出す、的なことだけが簡単にできるようにしておいて、あとはアプリでがんばってもらおう
MMOの場合、zone(分割されている場合、そのうちの1リージョン)がUniversalObjectになるだろう
zoneは自身の内部にユーザーオブジェクトへの参照を持っているがどのようにしてそのオブジェクトに対して通知を行うべきか
zoneにenterするときにリモートアドレスを渡しておきその接続に対してRPCする
[zone] -> [server] -> [client]
それだけではそのアドレスにつながっているコネクションのうちどれに書き込めばいいかわからないので
なにかidが必要になる。user id => addrのマップを持たせて、idからaddrを引き、そこからsocketを引いて通知する
(直接ユーザーのaddressをzoneに登録してそれを使えば検索のステップが1つ減るが、違う人の通知がくるバグが出そうな気がする)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment