くまのてサーバー

練習課題

サーバー内の(アクティブでは無い物含めて)乗り物すべてのリストを出すコマンドまたは何かしらの出力


プロジェクト名、ソリューション名:ClassLibrary1

でまた新たに作成


習作1:チャット欄で「:test」と発言するとサーバーログにFPSを返す


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    public class API : IModApi
    {
        public void InitMod(Mod _modInstance)
        {
            ModEvents.ChatMessage.RegisterHandler(ChatCheck);
        }

        public bool ChatCheck (ClientInfo _cInfo , EChatType _type , int _senderId , string _msg, string _mainName,
            bool _localizeMain , List _recipientEntityIds)
        {

            string messageLower = _msg.ToLower();
            Log.Out(messageLower);

            if ( messageLower == ":test")
            {

                string _fps = GameManager.Instance.fps.Counter.ToString();
                Log.Out("Test {0}", _fps);

        	return true;
            }
            return true;
        }  
    }
}

発言をトリガーにした機能のテスト

「:test」と発言するとサーバーFPS値を返す。

_msgを小文字に替えてそのメッセージ内容を検査し処理して、

そのままreturn trueで返して次の7dtd本体の処理に渡している。

場合によっては他のmodの処理がはいる。

ここをreturn falseにすると次の処理をすべてキャンセルできる。


ワンポイントクッキング

ChatCheckでchatを捕まえたあと _type を検査してGlobalならなにもせずにreturn falseとすれば

パーティ・同盟チャット機能は維持しつつ全体発言を無効化できるmodになる

システムの発言も止められてしまうので_cInfoか_senderIdがnullの時はtrueで返す必要はありそう

おためしあれ


習作2:チャット欄で「:test」と発言するとチャット欄に「Server」名義でサーバーFPSを返す。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    public class API : IModApi
    {
        public void InitMod(Mod _modInstance)
        {
            ModEvents.ChatMessage.RegisterHandler(ChatCheck);
        }

        public bool ChatCheck (ClientInfo _cInfo , EChatType _type , int _senderId , string _msg, string _mainName,
            bool _localizeMain , List _recipientEntityIds)
        { 

            string messageLower = _msg.ToLower();
            Log.Out(messageLower);

            string _sendername = "Server:";
            if ( messageLower == ":test")
            {

            string _fps = GameManager.Instance.fps.Counter.ToString();
            _cInfo.SendPackage(NetPackageManager.GetPackage().Setup(EChatType.Whisper, _senderId, _fps, _sendername, false, null));

            return false;
        }
        return true;
    }
}
}

次の目標、コンソールコマンド化してみる。

7dtd.illy.bzのallocsのソースコードを覗いてみる ConsoleCmdAbstract が鍵の様子。

テストとしてConsoleCmdAbstractを引っ張って、お手本通りに

GetDescription , GetCommands , Excute を同じように定義してみる。


習作3


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    public class API : IModApi
    {
        public void InitMod(Mod _modInstance)
        {

        }

    }
    public class GetFPS : ConsoleCmdAbstract
    {
        public override string GetDescription()
        {
            return ("Get FPS test."); 
        }
        public override string[] GetCommands()
        {
            return new[] {"getfps", string.Empty};
        }
        public override void Execute (List _params , CommandSenderInfo _senderInfo)
        {
            string _fps = GameManager.Instance.fps.Counter.ToString();
            Log.Out("Server FPS: " + _fps );
        }
    }   
}

telnetでサーバーへ接続

----------------------------------
help getfps
*** Command: getfps ***
No detailed help available.
Description: Get FPS test.
getfps
2022-04-24T13:58:08 202.042 INF Server FPS: 19.99999
----------------------------------

動いた。


GameManager.Instance.GetEntityあたりだとアクティブなのしか取れなさそうなので

vehiclemanagerを見てみる。

GetVehiclesを利用できれば良いのだがprivateで他所から使えないみたい。

vehicles.datに保存されているのは判っているので、これを読みだす部分を探す

Loadとreadだが肝心のreadがまたprivateなのでこれらルーチンを移植してみる。


途中Fileの参照先が無いとVisualStudioちゃんに怒られたのので

using System.IO;

を追加してみる。


習作4


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;


namespace ClassLibrary1
{
    public class API : IModApi
    {
        public void InitMod(Mod _modInstance)
        {

        }

    }
    public class GetFPS : ConsoleCmdAbstract
    {
        public override string GetDescription()
        {
            return ("Get FPS test."); 
        }
        public override string[] GetCommands()
        {
            return new[] {"getfps", string.Empty};
        }
        public override void Execute (List _params , CommandSenderInfo _senderInfo)
        {
            string _fps = GameManager.Instance.fps.Counter.ToString();
            Log.Out("Server FPS: " + _fps );
        }
    }

    public class GetVehicles : ConsoleCmdAbstract
    {
        private readonly List vehiclesUnloaded = new List();

        public override string GetDescription()
        {
            return ("Get all vehicles.");
        }
        public override string[] GetCommands()
        {
            return new[] { "getvehicles", string.Empty };
        }

        public override void Execute(List _params, CommandSenderInfo _senderInfo)
        {

            Load();
            for (int i = 0; i < vehiclesUnloaded.Count; i++)
            {
                string _name = vehiclesUnloaded[i].entityName;

                Log.Out("Test001" + _name);
            }
        }

        public void Load()
        {
            string text = string.Format("{0}/{1}", GameIO.GetSaveGameDir(), "vehicles.dat");
            if (!File.Exists(text))
            {
	        return;
            }

            try
            {
	        using (FileStream baseStream = File.OpenRead(text))
	        {
	            using (PooledBinaryReader pooledBinaryReader = MemoryPools.poolBinaryReader.AllocSync(_bReset: false))
	            {
		        pooledBinaryReader.SetBaseStream(baseStream);
		        read(pooledBinaryReader);
	            }
	        }
            }
            catch (Exception)
            {
	        text = string.Format("{0}/{1}", GameIO.GetSaveGameDir(), "vehicles.dat.bak");
	        if (File.Exists(text))
	        {
	            using (FileStream baseStream2 = File.OpenRead(text))
	            {
		        using (PooledBinaryReader pooledBinaryReader2 = MemoryPools.poolBinaryReader.AllocSync(_bReset: false))
		        {
		            pooledBinaryReader2.SetBaseStream(baseStream2);
		            read(pooledBinaryReader2);
		        }
	            }
	        }
            }

            Log.Out("VehicleManager2 {0}, loaded {1}", text, vehiclesUnloaded.Count);
        }

        private void read(PooledBinaryReader _br)
        {
            if (_br.ReadChar() != 'v' || _br.ReadChar() != 'd' || _br.ReadChar() != 'a' || _br.ReadChar() != 0)
            {
	        Log.Error("Vehicle file bad signature");
	        return;
            }

            if (_br.ReadByte() != 1)
            {
	        Log.Error("Vehicle file bad version");
	        return;
            }

            vehiclesUnloaded.Clear();
            int num = _br.ReadInt32();
            for (int i = 0; i < num; i++)
            {
	        EntityCreationData entityCreationData = new EntityCreationData();
	        entityCreationData.read(_br, _bNetworkRead: false);
	        vehiclesUnloaded.Add(entityCreationData);
                Log.Out("read #{0}, id {1}", i, entityCreationData.id);
            }
        }
    }
}


ジャイロを2台別々の場所に放置して誰もログインしていないワールドでのgetvehicleコマンド実行

2022-04-25T11:15:32 320.823 INF read #0, id 372>
2022-04-25T11:15:32 320.823 INF read #1, id 373
2022-04-25T11:15:32 320.823 INF VehicleManager2 /home/sdtd/data/kumaen09/MyGame/vehicles.dat, loaded 2
2022-04-25T11:15:32 320.823 INF Test001
2022-04-25T11:15:32 320.823 INF Test001

ログインしてジャイロを一台アクティブ状態にしても同じ結果なのでこれを元にシェイプアップしていく。

ついでにgetfpsの方も整えてみる。


EntityCreationDataをEnitityVehicleとして間違って扱っていたので

改めてEntityVehicleに変換を試みる。


MonoBehaviorを使うにはunityengine.core云々が出たので

UnityEngine.CoreModule

を参照に追加してみた。


・乗り物種類と座標の情報を出力。

・コンソールコマンドでの入力の場合はコンソール出力するよう。

を試してみる。


いつもの「この言語のコメントアウトってなんだっけ?」自分用メモ

/* */ で括るか//先頭でコメント


ついでに持ち主プレイヤーIDも出したいが、owneridやbelongplayerみたいなのを引っ張っても-1しか返ってこない。

vehicles.datをバイナリエディタで覗くとプレイーヤーIDらしきものは確認できたので取れないはずは無いと思うのでやり方が間違ってるのだろう。


(後日、なんやかんやでownerIDを取れるようになった。)


習作5


using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

namespace ClassLibrary1
{
    public class API : IModApi
    {
        public void InitMod(Mod _modInstance)
            {

            }       
        }
        public class GetFPS : ConsoleCmdAbstract
        {
        public override string GetDescription()
            {
                return ("Get FPS test."); 
            }
        public override string[] GetCommands()
        {
            return new[] {"getfps", string.Empty};
        }
        public override void Execute (List _params , CommandSenderInfo _senderInfo)
        {
            string _fps = GameManager.Instance.fps.Counter.ToString();
            SdtdConsole.Instance.Output(string.Format("Server FPS: " + _fps));
            return;
        }
    }

    public class GetVehicles : ConsoleCmdAbstract
    {
        private readonly List vehiclesUnloaded = new List();

        public override string GetDescription()
        {
            return ("Get all vehicles.");
        }
        public override string[] GetCommands()
        {
            return new[] { "getvehicles", string.Empty };
        }

        public override void Execute(List _params, CommandSenderInfo _senderInfo)
        {

            Load();
            World world = GameManager.Instance.World;

            ClientInfo ci = _senderInfo.RemoteClientInfo;
            if (ci != null)
            {
                SdtdConsole.Instance.Output(ci.CrossplatformId.CombinedString);
            }
            for (int i = 0; i < vehiclesUnloaded.Count; i++)
            {
                EntityCreationData entityCreationData = vehiclesUnloaded[i];

                EntityVehicle entityVehicle = world.GetEntity(entityCreationData.id) as EntityVehicle;

                entityVehicle = (EntityFactory.CreateEntity(entityCreationData) as EntityVehicle);

                SdtdConsole.Instance.Output(entityVehicle.GetOwner() + " : " + entityVehicle.position.x + "," + entityVehicle.position.z);

            }
            return;
        }

        public void Load()
        {
            string text = string.Format("{0}/{1}", GameIO.GetSaveGameDir(), "vehicles.dat");
            if (!File.Exists(text))
            {
                return;
            }

            try
            {
                using (FileStream baseStream = File.OpenRead(text))
                {
	            using (PooledBinaryReader pooledBinaryReader = MemoryPools.poolBinaryReader.AllocSync(_bReset: false))
	            {
                        pooledBinaryReader.SetBaseStream(baseStream);
		        read(pooledBinaryReader);
	            }
	        }
            }
            catch (Exception)
            {
	        text = string.Format("{0}/{1}", GameIO.GetSaveGameDir(), "vehicles.dat.bak");
                if (File.Exists(text))
                {
                    using (FileStream baseStream2 = File.OpenRead(text))
                    {
                        using (PooledBinaryReader pooledBinaryReader2 = MemoryPools.poolBinaryReader.AllocSync(_bReset: false))
                        {
                            pooledBinaryReader2.SetBaseStream(baseStream2);
                            read(pooledBinaryReader2);
                        }
                    }
                }
            }
        }

        private void read(PooledBinaryReader _br)
        {
            if (_br.ReadChar() != 'v' || _br.ReadChar() != 'd' || _br.ReadChar() != 'a' || _br.ReadChar() != 0)
            {
                Log.Error("Vehicle file bad signature");
                return;
            }

            if (_br.ReadByte() != 1)
            {
	        Log.Error("Vehicle file bad version");
	        return;
            }

            vehiclesUnloaded.Clear();
            int num = _br.ReadInt32();
            for (int i = 0; i < num; i++)
            {
	        EntityCreationData entityCreationData = new EntityCreationData();
	        entityCreationData.read(_br, _bNetworkRead: false);
	        vehiclesUnloaded.Add(entityCreationData);
            }
        }
    }
}

もう少し実運用に合わせた形にしていく。


・getfpsはmemの代わりに使いたいので出力情報・形式をmemに寄せる。
・getvehiclesはconsoleコマンド時は全ユーザー出力でプレイヤー使用時は自分のだけ出力にする。
・コマンド名の先頭にhjkw-を付けて他と被ることが無いようにしたい

ので一度全部書き直すので新規プロジェクトを立ち上げなおして名前から全部整えて再作成する。

練習ここまで

2022-5-12 4月21日からVisualStudio2022を使ってC#を用いてのDLL作成勉強を始めて、課程を見なおして見たけど
動くけど良くない書き方とか大分しているのに気づいた
まだ、わかんねー箇所が多いけどぼちぼち習得していくつもり