Last active
December 13, 2015 20:18
-
-
Save umegaya/4969064 to your computer and use it in GitHub Desktop.
ゾーンとユーザーのスケーリングについての設計
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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