継続を使用する並列分散フレームワークのUnity実装

Ryo Yasuda, Shinji Kono 並列信頼研

本研究における成果

概要

オンラインゲームにおけるデータ通信

オンラインゲームにおけるデータ通信

Christie の基礎概念

Christie の基礎概念

message
Christie を同一プロセスで複数インスタンス立ち上げた際の接続の構造図

Christie の基礎概念 annotationについて

DG を取り出すためにCG内に宣言した変数データにannotation をつける。annotationには以下の4つがある。

Topology Manager

Topology Manager

digraph test {
	node0 -> node1 [label="right"]
	node1 -> node2 [label="right"]
	node2 -> node0 [label="right"]
}
message

Java からの変更点

Java
public class StartHelloWorld extends StartCodeGear { }

@Override
protected void run(CodeGearManager cgm) { }

@Take String helloWorld;
C#
public class StartHelloWorld : StartCodeGear { }

public override void Run(CodeGearManager cgm) { }

[Take] string helloWorld;

Christie # のコード例

public class StartHelloWorld : StartCodeGear {
    
    public StartHelloWorld(CodeGearManager cgm) : base(cgm) { }

    public static void Main(string[] args) {
        CodeGearManager cgm = CreateCgm(10000);
        cgm.Setup(new HelloWorldCodeGear());
        cgm.Setup(new FinishHelloWorld());
        cgm.GetLocalDGM().Put("helloWorld", "hello");
        cgm.GetLocalDGM().Put("helloWorld", "world");        
    }
}
public class HelloWorldCodeGear : CodeGear {
    [Take] string helloWorld;
    
    public override void Run(CodeGearManager cgm) {
        Console.Write(helloWorld + " ");
        cgm.Setup(new HelloWorldCodeGear());
        cgm.GetLocalDGM().Put(helloWorld, helloWorld);
    }
}
public class FinishHelloWorld : CodeGear {
    [Take] private string hello;
    [Take] private string world;

    public override void Run(CodeGearManager cgm) {
        cgm.GetLocalDGM().Finish();
    }
}
  1. Main関数でCGM のインスタンス生成
  2. 2つのCG をsetupして待ち状態にする
  3. key:hellowWorld data:”hello” がTake される
  4. 変数が揃ったためStartHelloWorld のRun が実行される
  5. “hello” がprintされ、再び待ち状態になる。 key:hellow data:”hello”がput される
  6. key:hellowWorld data:”world” がTake され、4,5と同様に処理される
  7. 変数hello とworld がput され揃ったため、FinishHelloWorld のRun が実行され、プログラムは終了する

Unity

Christie # on Unityのコード例

public class StartHelloWorld : StartCodeGear {
    
    public StartHelloWorld(CodeGearManager cgm) : base(cgm) { }

    public  void RunCodeGear(CodeGearManager cgm) {
        cgm.Setup(new HelloWorldCodeGear());
        cgm.Setup(new FinishHelloWorld());
        cgm.GetLocalDGM().Put("helloWorld", "hello");
        cgm.GetLocalDGM().Put("helloWorld", "world");
    }
}
public class HelloWorld : MonoBehaviour {
    void Start() {
        CodeGearManager cgm = StartCodeGear.CreateCgm(10000);
        var helloWorld = new StartHelloWorld(cgm);
        helloWorld.RunCodeGear(cgm);
    }
}

Take annotationの実装

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Take { }
[AttributeUsage(AttributeTargets.Field)]
public class Take : Attribute { }

MessagePackの相違点

public class MessagePackExample {
    @Message
    public static class MyMessage {
        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);
    }
}

MessagePackの相違点

[MessagePackObject]
public class MyClass {
    [Key(0)]
    public int Age { get; set; }
    [Key(1)]
    public string FirstName { get; set; }
    [Key(2)]
    public string LastName { get; set; }

    static void Main(string[] args) {
        var mc = new MyClass {
            Age = 99,
            FirstName = "hoge",
            LastName = "huga",
        };

        byte[] bytes = MessagePackSerializer.Serialize(mc);
        MyClass mc2 = MessagePackSerializer.Deserialize<MyClass>(bytes);

        // [99,"hoge","huga"]
        var json = MessagePackSerializer.ConvertToJson(bytes);
        Console.WriteLine(json);
    }
}

ThreadPoolからTaskへの書き換え

ThreadPoolからTaskへの書き換え

public class PriorityThreadPoolExecutors {

    private static class PriorityThreadPoolExecutor extends ThreadPoolExecutor {
        private static final int DEFAULT_PRIORITY = 0;
        private static AtomicLong instanceCounter = new AtomicLong();

        public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                                int keepAliveTime, TimeUnit unit) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, (BlockingQueue) new PriorityBlockingQueue<ComparableTask>(10,
		ComparableTask.comparatorByPriorityAndSequentialOrder()));
        }

        @Override
        public void execute(Runnable command) {
                super.execute(command);
        }
    }
}
public class ThreadPoolExecutors {
    
    public ThreadPoolExecutors(int nWorkerThreads, int nIOThreads) {
        ThreadPool.SetMinThreads(nWorkerThreads, nIOThreads);
    }
    
    public void Execute(CodeGearExecutor command) {
        Task.Factory.StartNew(() => command.Run());
    }
}

Unityで使用されているライブラリとの比較

Unityで使用されている既存のライブラリとして、Photon Unity Networking 2(PUN2)、MLAPIと、Christie # の比較を行う。

  Christie # PUN2 MLAPI
通信方式 p2p クライアントサーバ方式 クライアントサーバ方式
プロトコル TCP TCP TCP
特徴 通信のためのIP address がプログラム直接記述されていない Photon Cloud でサーバを自前で用意する必要がない Unity公式でサポートされている RPC が使用可能

チート対策について

message
label を使用したデータ通信

実装の現状

まとめ