#
研究目的
- ゲームの通信方式にはクライアントサーバ方式とp2p方式がある
- データの安全性やチート対策などでクライアントサーバ方式が主流
- サーバに接続してマルチプレイなどのデータ同期を実現させているため、低速
- 高速かつ安全に通信を行たい
- 並列分散フレームワークChristieがある
- Christieを利用してp2pで通信を行う
- ゲーム開発で主に使用されているUnityに対応するためにChristieをC#へ書き換えを行う
#
今週の進捗
- 急にやる気が出て、毎日最低1commitするようにした
- socket(Connection)
- MessagePackのバージョンとC#に入れたMessagePack
- enum(CommandType)
#
(B3向け)Christieとは
- GearsOS(kono研で開発しているOS)に導入予定の分散フレームワーク
- CodeGearとDataGearという概念を持っており、これらを利用して分散的に通信を行う
- CodeGear → スレッドに値するもの プログラミング的処理(関数など)とDataGearを持っている
- DataGear → データ classのフィールド変数的な
- CodeGearManager, DataGearManager…
詳しいことは一木が説明してくれるはず
#
書き換え方針
- Annotation(C#だとAttribute)は完了
- CodeGear → DataGear, DataGearsが書き終わり → Command周りを書き終えた
残りのDataGear関連(DGM, RemoteDGM, WaitListなど)を書き終えたら、CodeGear関連を書き換える
年内くらいにはHelloWorldが動かせるといいなぁ
#
ジェネリクスの型情報について
1
2
3
4
5
6
7
8
9
|
public synchronized void put(String key, DataGear dg){
if (dataGears.containsKey(key)) {
dataGears.get(key).add(dg);
} else {
LinkedBlockingQueue<DataGear> queue = new LinkedBlockingQueue<DataGear>();
queue.add(dg);
dataGears.put(key, queue);
}
}
|
1
2
3
4
5
6
7
8
9
|
public void Put(string key, DataGear<object> dg) {
if (dataGears.ContainsKey(key)) {
dataGears[key].Enqueue(dg);
} else {
var queue = new ConcurrentQueue<DataGear<object>>();
queue.Enqueue(dg);
dataGears.Add(key, queue);
}
}
|
#
Socketについて
プログラミングでほぼ初めてSocket通信のプログラムを書きました…
接続先もAcceptしてないとIPとか取れないとか初めて知った
なんも分からん状態だったので、ChristieのSocketの部分を見つつJavaの例題をやってなんとなく理解してから
C#でも同じように例題動かしてみて書き換えしました
javaだとSocketクラスが割となんでもしてくれる感じ
C#だと役割が分散してる
1
2
3
4
5
|
Socket s = new Socket("localhost",3333);
DataOutputStream dout=new DataOutputStream(s.getOutputStream());
dout.writeUTF("Hello World");
dout.flush();
|
1
2
3
4
5
6
7
8
9
10
|
IPHostEntry host = Dns.GetHostEntry ("localhost");
IPAddress ipAddress = host.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint (ipAddress, 11000);
Socket sender = new Socket (ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
sender.Connect (remoteEP);
byte[] msg = Encoding.ASCII.GetBytes ("Hello World");
int bytesSent = sender.Send (msg);
|
#
MessagePackのバージョン云々
Christieに入っているMessagePackのバージョンは0.6.x
シリアライズするクラスには@Messageをつける必要がある
バージョンが分からずにググっているとバージョンの壁に突き当たるかも
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import org.msgpack.MessagePack;
import org.msgpack.annotation.Message;
public class Main1 {
@Message // Annotation
public static class MyMessage {
// public fields are serialized.
public String name;
public double version;
}
public static void main(String[] args) throws Exception {
MyMessage src = new MyMessage();
src.name = "msgpack";
src.version = 0.6;
MessagePack msgpack = new MessagePack();
// Serialize
byte[] bytes = msgpack.write(src);
// Deserialize
MyMessage dst = msgpack.read(bytes, MyMessage.class);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// Serialize with MessagePacker.
// MessageBufferPacker is an optimized version of
// MessagePacker for packing data into a byte array
MessageBufferPacker packer = MessagePack.newDefaultBufferPacker();
packer
.packInt(1)
.packString("leo")
.packArrayHeader(2)
.packString("xxx-xxxx")
.packString("yyy-yyyy");
byte[] msgpack = packer.toByteArray()
// Deserialize with MessageUnpacker
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(msgpack);
int id = unpacker.unpackInt(); // 1
String name = unpacker.unpackString(); // "leo"
int numPhones = unpacker.unpackArrayHeader(); // 2
String[] phones = new String[numPhones];
for (int i = 0; i < numPhones; ++i) {
phones[i] = unpacker.unpackString(); // phones = {"xxx-xxxx", "yyy-yyyy"}
}
unpacker.close();
|
#
C#のMessagePackについて
- C#向けのMessagePackはmsgpackが作っているものとCSharpが作っているものの2つがある
- CSharp版は日本人が作成している
- Unityなどでも使用できるらしい
- cliよりも爆速であるらしい
2種類あるというのも書き換えで勘違いしそうになった要因
シリアライズするクラスにはAttributeをつける必要があるため、こちらを採用した
1
2
3
4
5
6
7
8
9
10
11
|
[MessagePackObject]
public class RemoteMessage {
[Key("type")]
public int type; // コマンドタイプ
[Key("fromDmgName")]
public string fromDmgName; // 送り元のDgmName REPLYの時に使用
[Key("key")]
public string key;
[Key("clazz")]
public string clazz;
}
|
#
javaとC#のenumの違いについて
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public enum CommandType {
PUT,
TAKE,
PEEK,
REMOTETAKE,
REMOTEPEEK,
REPLY,
CLOSE,
FINISH;
public int id;//コマンドのid
public static HashMap<Integer, CommandType> hash = new HashMap<Integer, CommandType>();//コマンド対応表
private static int lastId = 0;//コマンドの総数
private CommandType() {
this.id = incrementLastId();
}//for init
private int incrementLastId() {
return ++lastId;
}
public static CommandType getCommandTypeFromId(int id) {
return hash.get(id);
}
// 各コマンドが初期化された最後に呼ばれる
static {
for (CommandType type : CommandType.values()) {
hash.put(type.id, type);
}
}
}
|
- C#ではenumにフィールド変数やメソッドをはやすことができない
- ヘルパークラスを作って対応
- かなりややこしい感じに 想定していた動作になってない
HashMap(Dectionary)をstaticで返さないといけないのがヘルパクラスだと面倒
javaは最初にenumが呼び出された時に1回だけ初期化動作がされる
→ヘルパクラスを使って最初の一回だけ判断して初期化処理を作るのは面倒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class CommandType {
private static int lastId = 0; // コマンドの総数
public readonly int id = ++lastId; // コマンドのid コンストラクタが呼ばれるたびにlastIdが++される
public static readonly Dictionary<int, CommandType> hash = new Dictionary<int, CommandType>();
private CommandType() {
hash.Add(this.id, this);
}
/// <summary>
/// idよりCommandTypeを返す
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static CommandType GetCommandTypeFromId(int id) {
return hash[id];
}
public static readonly CommandType PUT = new CommandType();
public static readonly CommandType TAKE = new CommandType();
public static readonly CommandType PEEK = new CommandType();
public static readonly CommandType REMOTETAKE = new CommandType();
public static readonly CommandType REMOTEPEEK = new CommandType();
public static readonly CommandType REPLY = new CommandType();
public static readonly CommandType CLOSE = new CommandType();
public static readonly CommandType FINISH = new CommandType();
}
|