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; chat::chatobj->sysmsg(CodeConv::tstring(_T("*** ")) + o.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 (is_mandatory) chat::chatobj->sysmsg(CodeConv::tstring(_T("*** ")) + o.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; // スクリプトは使用不能 } }
/* ファイルリスト走査 */ 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*/ }
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 }
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; } }
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; }
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()); }
/* 非負整数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(); }
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; }
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; }
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()); } } }
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; } } }
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()); } }
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; } }
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; }