Beispiel #1
0
void aiscript::calcCall_threaded(GameTable* gameStat) {
	CodeConv::tostringstream o;
	o << _T("AIの副露判定に入ります。プレイヤー [") << (int)gameStat->CurrentPlayer.Passive << _T("]");
	info(o.str().c_str());
	gameStat->statOfPassive().DeclarationFlag.Chi = chiiNone; // リセット
	gameStat->statOfPassive().DeclarationFlag.Pon =
		gameStat->statOfPassive().DeclarationFlag.Kan =
		gameStat->statOfPassive().DeclarationFlag.Ron = false;
	if (callFunc(gameStat, gameStat->CurrentPlayer.Passive, fncname_call[gameStat->KangFlag.chankanFlag], (gameStat->KangFlag.chankanFlag == 0))) {
		return;
	} else {
		/* 実行完了 */
		int flag = 0;
		MeldCallID meldtype = (MeldCallID)lua_tointegerx(status[gameStat->CurrentPlayer.Passive].state, -2, &flag);
		if (!flag) {
			warn(_T("1番目の返り値が数値ではありません。無視します。"));
		} else {
			switch (meldtype) {
				case meldNone: break;
				case meldRon: gameStat->statOfPassive().DeclarationFlag.Ron = true; break;
				case meldKan: gameStat->statOfPassive().DeclarationFlag.Kan = true; break;
				case meldPon: gameStat->statOfPassive().DeclarationFlag.Pon = true; break;
				case meldChiiLower: gameStat->statOfPassive().DeclarationFlag.Chi = chiiLower; break;
				case meldChiiMiddle: gameStat->statOfPassive().DeclarationFlag.Chi = chiiMiddle; break;
				case meldChiiUpper: gameStat->statOfPassive().DeclarationFlag.Chi = chiiUpper; break;
				default: warn(_T("1番目の返り値が正しくありません。無視します。")); break;
			}
		}
		lua_pop(status[gameStat->CurrentPlayer.Passive].state, 1);
		return;
	}
}
Beispiel #2
0
void Data::decompress(int FileID_) {
#ifdef _WIN32
	DWORD size = 0;
#else /*_WIN32*/
	size_t size = 0;
#endif /*_WIN32*/
	const uint8_t* compressedBuf = nullptr;
	int result;
	LoadFileInResource(FileID_, LZMA_STREAM, size, compressedBuf);
	assert(size > 13);
	uint8_t* compressedData = (uint8_t *)malloc(size+1);
	memcpy(compressedData, compressedBuf, size);
	compressedData[size] = 0;
	decompressedSize = *((size_t *)(compressedData+5));
	DecompressedData = (uint8_t *)malloc(decompressedSize);
	result = LzmaUncompress(DecompressedData, &decompressedSize,
		(const uint8_t *)(compressedData+13),
		(SizeT *)&size, (const uint8_t *)compressedData, 5);
	free(compressedData); compressedData = nullptr;
	if (result != SZ_OK) {
		CodeConv::tostringstream o;
		o << _T("LZMAストリームのデコードに失敗しました。ファイルが壊れている虞があります。") <<
			_T("エラーコード: ") << result;
		Raise(EXCEPTION_MJCORE_DECOMPRESSION_FAILURE, o.str().c_str());
	}
	else {
		info(_T("LZMAストリームをデコードしました。"));
	}
	return;
}
Beispiel #3
0
CodeConv::tstring posPrefix(LPCTSTR file, int line, CodeConv::tstring msg) {
	CodeConv::tostringstream o;
	o << _T("(")
		<< ( (CodeConv::tstring(file).rfind(_T("\\"))) == CodeConv::tstring::npos ?
		file : file + (CodeConv::tstring(file).rfind(_T("\\"))) + 1 )
		<< _T(":") << line << _T(") ") << msg << std::flush;
	return CodeConv::tstring(o.str());
}
Beispiel #4
0
/* 非負整数1桁なら全角・それ以外は半角 */
CodeConv::tstring intstr(int val) {
	LPCTSTR hanstr[10] = {
		_T("0"), _T("1"), _T("2"), _T("3"), _T("4"),
		_T("5"), _T("6"), _T("7"), _T("8"), _T("9"),
	};
	CodeConv::tostringstream o;
	if ((val <= 9) && (val >= 0)) o << hanstr[val];
	else o << val;
	return o.str();
}
Beispiel #5
0
EndType procdahai(GameTable* const gameStat, DiscardTileNum& DiscardTileIndex) {
	EndType RoundEndType = Continuing;
	{
		CodeConv::tostringstream o;
		o << _T("プレイヤー [") << (int)gameStat->CurrentPlayer.Active <<
			_T("] 打牌タイプ [") << (int)DiscardTileIndex.type <<
			_T("] 手牌番号 [") << (int)DiscardTileIndex.id << _T("]");
		info(o.str().c_str());
	}
	/* 立直していない同順振聴ならその期限のため振聴を解除する */
	if (!gameStat->statOfActive().RichiFlag.RichiFlag)
		gameStat->statOfActive().DoujunFuriten = false;
	/* 自摸和の処理 */
	if (DiscardTileIndex.type == DiscardTileNum::Agari) {
		RoundEndType = procDahaiSubAgari(gameStat, DiscardTileIndex);
		if (RoundEndType != Continuing) return RoundEndType;
	}
	/* 九種九牌が宣言された場合 */
	if ((!gameStat->chkGameType(SanmaS)) && (DiscardTileIndex.type == DiscardTileNum::Kyuushu)) {
		RoundEndType = procDahaiSubKyuushu(gameStat, DiscardTileIndex);
		if (RoundEndType != Continuing) return RoundEndType;
	}
	/* 打牌を牌譜に記録する */
	if ((DiscardTileIndex.type == DiscardTileNum::Normal) ||
		(DiscardTileIndex.type == DiscardTileNum::Riichi) ||
		(DiscardTileIndex.type == DiscardTileNum::OpenRiichi))
		haifu::haifurecmota(gameStat, DiscardTileIndex);
	/* 花牌を抜いた場合の処理 */
	if (!gameStat->chkGameType(SanmaS)) {
		RoundEndType = procDahaiSubFlower(gameStat, DiscardTileIndex);
		if (RoundEndType != Continuing) return RoundEndType;
	}
	/* 暗槓・加槓するときの処理 */
	{
		RoundEndType = procDahaiSubKan(gameStat, DiscardTileIndex);
		if (RoundEndType != Continuing) return RoundEndType;
	}
	gameStat->KangFlag.kangFlag = false; // 嶺上開花のフラグを降ろす
	gameStat->PaoFlag[pyMinkan].paoPlayer = gameStat->PaoFlag[pyMinkan].agariPlayer = -1;
#ifndef GUOBIAO
	/* 立直をするときの処理 */
	if ((DiscardTileIndex.type == DiscardTileNum::Riichi) ||
		(DiscardTileIndex.type == DiscardTileNum::OpenRiichi))
		procDahaiSubRiichi(gameStat, DiscardTileIndex);
	/* 戻牌天和フラグ */
	if ((gameStat->statOfActive().renpaiTenhohStat == 0) &&
		(ShantenAnalyzer::calcShanten(gameStat, gameStat->CurrentPlayer.Active, shantenAll) == -1))
		gameStat->statOfActive().renpaiTenhohStat = 1;
	else if (gameStat->statOfActive().renpaiTenhohStat == 1)
		gameStat->statOfActive().renpaiTenhohStat = -1;
#endif /* GUOBIAO */
	/* 事後処理 */
	procDahaiSubPost(gameStat, DiscardTileIndex);
	return Continuing;
}
Beispiel #6
0
void translateException(unsigned int code, _EXCEPTION_POINTERS* ep) {
	CodeConv::tostringstream lmsg;
	lmsg << _T("構造化例外 ") <<
	std::hex << std::setw(8) << std::setfill(_T('0')) << ep->ExceptionRecord->ExceptionCode <<
	_T(" をC++例外に変換します。");
	info(lmsg.str().c_str());

	CONTEXT context; memcpy(&context, ep->ContextRecord, sizeof(CONTEXT));
	traceLog(ep->ContextRecord, nullptr, 0);
	throw ep;
}
Beispiel #7
0
	DLL void send (unsigned char SendingMsg) { // サーバーからの送信
		for (unsigned int i = 1; i < NumberOfPlayers; ++i) {
			try {
				if (sockets[i]&&(sockets[i]->connected()))
					sockets[i]->putc(SendingMsg);
			} catch (socket_error& err) {
				CodeConv::tostringstream o;
				o << _T("クライアント [") << i << _T("] への送信に失敗 エラーコード [") << err.error_code() << _T(']');
				error(o.str().c_str());
			}
		}
	}
Beispiel #8
0
/* ファイルリスト走査 */
void aiscript::FileSelector::filelist() {
	std::string confPath = confpath::confPath();
	if (confPath.empty()) confPath = ".";
#ifdef _WIN32
#ifdef GUOBIAO
	std::string scriptPath = confPath + std::string("\\gbai");
#else /* GUOBIAO */
	std::string scriptPath = confPath + std::string("\\ai");
#endif /* GUOBIAO */
	std::string scriptFiles = scriptPath + std::string("\\*.lua");
	files.clear();
	WIN32_FIND_DATAA finddat;
	info(_T("AIスクリプトを検索します"));

	/* 検索開始 */
	HANDLE h = FindFirstFileA(scriptFiles.c_str(), &finddat);
	if (h == INVALID_HANDLE_VALUE) {
		error(_T("ファイル検索できません!!")); return;
	}
	do { // 検索
		if (!strcmp(finddat.cFileName + strlen(finddat.cFileName) - 4, ".lua")) {
			CodeConv::tostringstream o; o << _T("検出: ") << finddat.cFileName; info(o.str().c_str());
			files.push_back(scriptPath + std::string("\\") + std::string(finddat.cFileName));
		}
	} while (FindNextFileA(h, &finddat));
	/* 検索完了! */
	FindClose(h);
#else /*_WIN32*/
#ifdef GUOBIAO
	std::string scriptPath = confPath + std::string("/gbai");
#else /* GUOBIAO */
	std::string scriptPath = confPath + std::string("/ai");
#endif /* GUOBIAO */
	files.clear();
	info(_T("AIスクリプトを検索します"));

	/* 検索開始 */
	DIR* dir = opendir(scriptPath.c_str());
	dirent* dirp;
	if (!dir) {
		error(_T("ファイル検索できません!!")); return;
	}
	while ((dirp = readdir(dir))) { // 検索
		if (!strcmp(dirp->d_name + strlen(dirp->d_name) - 4, ".lua")) {
			CodeConv::tostringstream o; o << _T("検出: ") << (dirp->d_name); info(o.str().c_str());
			files.push_back(scriptPath + std::string("/") + std::string(dirp->d_name));
		}
	}
	/* 検索完了! */
	closedir(dir);
#endif /*_WIN32*/
}
Beispiel #9
0
bool aiscript::callFunc(const GameTable* const gameStat, PlayerID PlayerID, const char* const function_name, bool is_mandatory) {
	if (status[PlayerID].scriptLoaded) { /* 正しく読み込まれているなら */
		try { /* シンボルがあればよし、なかったら例外処理 */
			lua_getglobal(status[PlayerID].state, function_name);
		} catch (...) { /* シンボルがなかったらエラーになるので例外処理をする */
			CodeConv::tostringstream o;
			if (is_mandatory) {
				o << _T("グローバルシンボル [") << CodeConv::EnsureTStr(function_name) << _T("] の取得に失敗しました"); error(o.str().c_str());
				info(_T("このスクリプトは使用できません。デフォルトAI(ツモ切り)に切り替えます。"));
				status[PlayerID].scriptLoaded = false;
				error(o.str().c_str());
				return true;
			} else {
				o << _T("グローバルシンボル [") << CodeConv::EnsureTStr(function_name) << _T("] の取得に失敗しました。無視します。");
				warn(o.str().c_str());
				return true;
			}
		}
		GameStatToLuaTable(status[PlayerID].state, gameStat);
		if (int errcode = lua_pcall(status[PlayerID].state, 1, 2, 0)) {
			/* 実行失敗! */
			CodeConv::tostringstream o;
			switch (errcode) {
			case LUA_ERRRUN:
				o << _T("スクリプトの実行時エラー [") <<
					CodeConv::DecodeStr(lua_tostring(status[PlayerID].state, -1)) /* エラーメッセージ */ <<
					_T("]");
				lua_pop(status[PlayerID].state, 1);
				break;
			case LUA_ERRMEM: o << _T("メモリの割当に失敗しました。"); break;
			case LUA_ERRERR: o << _T("メッセージハンドラ実行中のエラーです。"); break;
			case LUA_ERRGCMM: o << _T("ガーベジコレクション実行中のエラーです。"); break;
			}
			error(o.str().c_str());
			if (std::string(function_name) == std::string(fncname_discard))
				warn(_T("関数呼び出しに失敗したため、ツモ切りとみなします"));
			else
				warn(_T("関数呼び出しに失敗したため、無視します"));
			return true;
		} else {
			/* 実行完了 */
			return false;
		}
	} else {
		if (std::string(function_name) == std::string(fncname_discard))
			warn(_T("スクリプトがロードされていないか、使用できないため、ツモ切りとみなします"));
		else
			warn(_T("スクリプトがロードされていないか、使用できないため、無視します"));
		return true; // スクリプトは使用不能
	}
}
Beispiel #10
0
void traceLog(CONTEXT* ex, int* const addrList, int addrListSize) {
#ifdef _DEBUG
#ifdef _M_IX86
	/* スタックトレース(I386専用です) */
	CodeConv::tostringstream lmsg;
	if (addrList != nullptr) memset(addrList, 0, addrListSize);

	HANDLE hProcess = GetCurrentProcess();
	DWORD disp;
	HANDLE hThread = GetCurrentThread();

	PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, 16384);
	pSymbol->SizeOfStruct = 16384; pSymbol->MaxNameLength = 16384 - sizeof(IMAGEHLP_SYMBOL);
	SymInitialize(GetCurrentProcess(), nullptr, TRUE);

	STACKFRAME stackFrame; memset(&stackFrame, 0, sizeof(stackFrame));
	stackFrame.AddrPC.Offset = ex->Eip;
	stackFrame.AddrFrame.Offset = ex->Ebp;
	stackFrame.AddrStack.Offset = ex->Esp;
	stackFrame.AddrPC.Mode = stackFrame.AddrFrame.Mode =
		stackFrame.AddrStack.Mode = AddrModeFlat;

	CONTEXT context;
	memcpy(&context,  ex, sizeof(CONTEXT));
	context.ContextFlags = CONTEXT_FULL;

	for (unsigned int i = 0; true; i++) {
		if (!StackWalk(IMAGE_FILE_MACHINE_I386, hProcess, hThread,
			&stackFrame, &context, nullptr, SymFunctionTableAccess, SymGetModuleBase, nullptr)) break;
		if (stackFrame.AddrPC.Offset == 0) break;

		if (addrList != nullptr) {
			addrList[i] = stackFrame.AddrPC.Offset;
			if (i >= (addrListSize / sizeof(int))) break;
		} else {
			if (SymGetSymFromAddr(hProcess, stackFrame.AddrPC.Offset, &disp, pSymbol))
				lmsg << std::hex << std::setw(8) << std::setfill(_T('0')) << stackFrame.AddrPC.Offset <<
				_T(" ") << pSymbol->Name << _T("() + ") <<
				std::setfill(_T('0')) << disp;
			else lmsg << std::hex << std::setw(8) << std::setfill(_T('0')) << stackFrame.AddrPC.Offset <<
				_T(" Unknown");
			debug(lmsg.str().c_str()); lmsg.str(_T(""));
		}
	}
	
	SymCleanup(hProcess);
	GlobalFree(pSymbol);
#endif
#endif
}
Beispiel #11
0
void aiscript::calcDiscard_threaded(DiscardTileNum& answer, const GameTable* gameStat) {
	CodeConv::tostringstream o;
	o << _T("AIの打牌処理に入ります。プレイヤー [") << (int)gameStat->CurrentPlayer.Active << _T("]");
	info(o.str().c_str());
	if (callFunc(gameStat, gameStat->CurrentPlayer.Active, fncname_discard, true)) {
		answer = DiscardThrough;
		return;
	} else {
		/* 実行完了 */
		int flag;
		answer.type = (DiscardTileNum::discardType)lua_tointegerx(status[gameStat->CurrentPlayer.Active].state, -2, &flag);
		if (!flag) {
			warn(_T("1番目の返り値が数値ではありません。通常の打牌とみなします。"));
			answer.type = DiscardTileNum::Normal; // fallback
		} else if ((answer.type < DiscardTileNum::Normal) || (answer.type > DiscardTileNum::Disconnect)) {
			warn(_T("1番目の返り値が正しくありません。通常の打牌とみなします。"));
			answer.type = DiscardTileNum::Normal; // fallback
		}
		if ((answer.type == DiscardTileNum::Agari) || (answer.type == DiscardTileNum::Kyuushu) ||
			(answer.type == DiscardTileNum::Disconnect)) { // 番号指定が不要な場合
				answer.id = NumOfTilesInHand - 1; // 2番めの返り値は無視
		} else {
			int i = lua_tointegerx(status[gameStat->CurrentPlayer.Active].state, -1, &flag);
			if (!flag) {
				warn(_T("2番目の返り値が数値ではありません。ツモ切りとみなします。"));
				answer.id = NumOfTilesInHand - 1; // fallback
			} else if ((i >= 1)&&(i <= NumOfTilesInHand)) {
				answer.id = i - 1; // オリジンを1にする仕様……
			} else if ((i <= -1)&&(i >= -((int)NumOfTilesInHand))) { // マイナスを指定した場合の処理
				answer.id = NumOfTilesInHand + i;
			} else {
				warn(_T("2番目の返り値が範囲外です。ツモ切りとみなします。"));
				answer.id = NumOfTilesInHand - 1; // fallback
			}
		}
		lua_pop(status[gameStat->CurrentPlayer.Active].state, 2);
		return;
	}
}
Beispiel #12
0
void Data::verify(LPCTSTR Description_, const uint8_t* const expectedDigest_) {
	memset(actualDigest, 0, sizeof(actualDigest)); bool mdUnmatch = false;
	calcSHA256();
	for (int i = 0; i < 32; i++) {
		if (expectedDigest_[i] != actualDigest[i]) mdUnmatch = true;
	}
	if (mdUnmatch) {
		CodeConv::tostringstream o;
		o << Description_ << _T("のSHA256ハッシュ値が一致しませんでした。") <<
			_T("ファイルが壊れている虞があります。") << std::endl <<
			_T("期待されるハッシュ値: ") <<
			CodeConv::EnsureTStr(bytesToHexString(std::vector<uint8_t>(expectedDigest_, expectedDigest_ + 32))) << std::endl <<
			_T("実際のハッシュ値: ") <<
			CodeConv::EnsureTStr(bytesToHexString(std::vector<uint8_t>(actualDigest, actualDigest + 32)));
		Raise(EXCEPTION_MJCORE_HASH_MISMATCH, o.str().c_str());
	}
	else {
		CodeConv::tostringstream o;
		o << Description_ << _T("のSHA256ハッシュ値の照合に成功しました。");
		info(o.str().c_str());
	}
}
Beispiel #13
0
void aiscript::readfile(aiscript::ScriptStates* const L, const char* const filename) {
	if (int errcode = luaL_loadfile(L->state, filename)) { /* ファイルを読み込み。成功したら0を返す */
		/* 読み込み失敗した時の処理 */
		CodeConv::tostringstream o;
		o << _T("スクリプトファイル [") << CodeConv::EnsureTStr(filename) << _T("] の読み込みに失敗しました。");
		switch (errcode) {
			case LUA_ERRFILE: o << _T("ファイルが開けません。"); break;
			case LUA_ERRSYNTAX: o << _T("構文が正しくありません。"); break;
			case LUA_ERRMEM: o << _T("メモリの割当に失敗しました。"); break;
			case LUA_ERRGCMM: o << _T("ガーベジコレクション実行中のエラーです。"); break;
		}
		error(o.str().c_str());
		chat::chatobj->sysmsg(CodeConv::tstring(_T("*** ")) + o.str());
	} else {
		CodeConv::tostringstream o;
		o << _T("スクリプトファイル [") << CodeConv::EnsureTStr(filename) << _T("] を読み込みました。");
		info(o.str().c_str());
		if (int errcode = lua_pcall(L->state, 0, LUA_MULTRET, 0)) {
			/* 実行失敗! */
			CodeConv::tostringstream o;
			switch (errcode) {
			case LUA_ERRRUN:
				o << _T("スクリプトの実行時エラー [") <<
					lua_tostring(L->state, -1) /* エラーメッセージ */ <<
					_T("]");
				lua_pop(L->state, 1);
				break;
			case LUA_ERRMEM: o << _T("メモリの割当に失敗しました。"); break;
			case LUA_ERRERR: o << _T("メッセージハンドラ実行中のエラーです。"); break;
			case LUA_ERRGCMM: o << _T("ガーベジコレクション実行中のエラーです。"); break;
			}
			error(o.str().c_str());
			chat::chatobj->sysmsg(CodeConv::tstring(_T("*** ")) + o.str());
		} else {
			/* 実行完了 */
			info(_T("スクリプトの実行に成功しました"));
			L->scriptLoaded = true;
		}
	}
}
Beispiel #14
0
void SeatShuffler::shuffleSeat () {
    if (EnvTable::Instantiate()->GameMode == EnvTable::Server)
        for (PlayerID i = 0; i < Players; ++i)
            if (EnvTable::Instantiate()->PlayerDat[i].RemotePlayerFlag > 1)
                mihajong_socket::listen(SOCK_CHAT - 1 + EnvTable::Instantiate()->PlayerDat[i].RemotePlayerFlag,
                                        PORT_CHAT - 1 + EnvTable::Instantiate()->PlayerDat[i].RemotePlayerFlag);
    // 退避
    InfoByPlayer<EnvTable::PlayerLabel> TmpPlayerDat;
    for (PlayerID i = 0; i < Players; i++) {
        TmpPlayerDat[i].PlayerName = EnvTable::Instantiate()->PlayerDat[i].PlayerName;
        TmpPlayerDat[i].RemotePlayerFlag = EnvTable::Instantiate()->PlayerDat[i].RemotePlayerFlag;
    }
    std::vector<PlayerID> TmpPosition;
    for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++) TmpPosition.push_back(i);
    // 場決め
    if (EnvTable::Instantiate()->GameMode != EnvTable::Client) {
        // 場決め処理
        std::random_shuffle(TmpPosition.begin(), TmpPosition.end(),
        [] (unsigned max) {
            return RndNum::rnd(max);
        });
        // サーバーであれば結果を送信
        if (EnvTable::Instantiate()->GameMode == EnvTable::Server)
            for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++)
                mihajong_socket::server::send(TmpPosition[i]);
    } else {
        // クライアントであれば受信する
        for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++) {
            int receivedByte;
            while ((receivedByte = mihajong_socket::getc(0)) == -1) // 受信待ち
                threadYield();
            TmpPosition[i] = receivedByte;
        }
    }
    // シャッフル結果を書き込み
    for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++) {
        EnvTable::Instantiate()->PlayerDat[TmpPosition[i]].PlayerName = TmpPlayerDat[i].PlayerName;
        EnvTable::Instantiate()->PlayerDat[TmpPosition[i]].RemotePlayerFlag = TmpPlayerDat[i].RemotePlayerFlag;
        posarry[i] = TmpPosition[i];
    }

    // リモートとしてマーク
    /*PlayerID tmpPlayer = TmpPosition[ClientNumber];
    if (EnvTable::Instantiate()->GameMode == EnvTable::Client)
    	for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++)
    		EnvTable::Instantiate()->PlayerDat[TmpPosition[i]].RemotePlayerFlag =
    		(i != tmpPlayer) ? 1 : 0;*/

    {
        CodeConv::tostringstream o;
        o << _T("ClientNumber [") << (int)ClientNumber << _T("]");
        debug(o.str().c_str());
    }
    {
        CodeConv::tostringstream o;
        o << _T("TmpPosition ");
        for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++)
            o << (i ? _T(" ") : _T("[")) << (int)TmpPosition[i];
        o << _T("]");
        debug(o.str().c_str());
    }
    {
        CodeConv::tostringstream o;
        o << _T("Remote? ");
        for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++)
            o << (i ? _T(" ") : _T("[")) << (int)EnvTable::Instantiate()->PlayerDat[i].RemotePlayerFlag;
        o << _T("]");
        debug(o.str().c_str());
    }
    {
        CodeConv::tostringstream o;
        o << _T("Name ");
        for (PlayerID i = 0; i < ACTUAL_PLAYERS; i++)
            o << (i ? _T(" [") : _T("[")) << EnvTable::Instantiate()->PlayerDat[i].PlayerName << _T("]");
        debug(o.str().c_str());
    }

    return;
}