From 49a4613d94d8e500e5bd3c259693fb5ccec4612e Mon Sep 17 00:00:00 2001 From: faketruth Date: Wed, 22 Aug 2012 23:05:12 +0000 Subject: [PATCH] Added a RateCompareString function to StringUtils Created a preprocessor template (define) for DoWith* functions Exported cWorld::FindAndDoWithPlayer(), cRoot::FindAndDoWithPlayer() and cRoot::ForEachPlayer() to Lua Added a function FindAndDoWithPlayer to cRoot and cWorld. It takes a part of a player name and finds a single player based on that. Fixed Core's MOTD to contain the correct URL to the MCServer site Fixed Core /kick command Fixed Core's WebAdmin kick git-svn-id: http://mc-server.googlecode.com/svn/trunk@779 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- MCServer/Plugins/Core/kick.lua | 28 ++-- MCServer/Plugins/Core/main.lua | 2 +- MCServer/Plugins/Core/motd.lua | 2 +- MCServer/Plugins/Core/web_playerlist.lua | 14 +- source/ManualBindings.cpp | 176 ++++++++++++----------- source/StringUtils.cpp | 27 ++++ source/StringUtils.h | 3 + source/cRoot.cpp | 53 +++++++ source/cRoot.h | 5 +- source/cWorld.cpp | 36 +++++ source/cWorld.h | 3 + 11 files changed, 245 insertions(+), 104 deletions(-) diff --git a/MCServer/Plugins/Core/kick.lua b/MCServer/Plugins/Core/kick.lua index ff4f8a705..ce8bd7a31 100644 --- a/MCServer/Plugins/Core/kick.lua +++ b/MCServer/Plugins/Core/kick.lua @@ -3,25 +3,27 @@ function HandleKickCommand( Split, Player ) Player:SendMessage( cChatColor.Green .. "Usage: /kick [Player] " ) return true end + + local FoundPlayerCallback = function( OtherPlayer ) + local Reason = "You have been kicked" + if( #Split > 2 ) then + Reason = table.concat(Split, " ", 3) + end - local World = Player:GetWorld() - local OtherPlayer = World:GetPlayer( Split[2] ) - if( OtherPlayer == nil ) then + local Server = cRoot:Get():GetServer() + LOGINFO( Player:GetName() .. " is kicking " .. OtherPlayer:GetName() .. " ( "..Reason..") " ) + Server:SendMessage( "Kicking " .. OtherPlayer:GetName() ) + + local ClientHandle = OtherPlayer:GetClientHandle() + ClientHandle:Kick( Reason ) + end + + if( cRoot:Get():FindAndDoWithPlayer( Split[2], FoundPlayerCallback ) == false ) then Player:SendMessage( cChatColor.Green .. "Could not find player " .. Split[2] ) return true end - local Reason = "You have been kicked" - if( #Split > 2 ) then - Reason = table.concat(Split, " ", 3) - end - local Server = cRoot:Get():GetServer() - LOGINFO( Player:GetName() .. " is kicking " .. OtherPlayer:GetName() .. " ( "..Reason..") " ) - Server:SendMessage( "Kicking " .. OtherPlayer:GetName() ) - - local ClientHandle = OtherPlayer:GetClientHandle() - ClientHandle:Kick( Reason ) return true end \ No newline at end of file diff --git a/MCServer/Plugins/Core/main.lua b/MCServer/Plugins/Core/main.lua index c813f19f7..6b4af5533 100644 --- a/MCServer/Plugins/Core/main.lua +++ b/MCServer/Plugins/Core/main.lua @@ -22,7 +22,7 @@ function Initialize( Plugin ) PluginManager:AddHook(Plugin, cPluginManager.HOOK_BLOCK_DIG) PluginManager:AddHook(Plugin, cPluginManager.HOOK_KILLED) PluginManager:AddHook(Plugin, cPluginManager.HOOK_CRAFTING_NO_RECIPE) - PluginManager:AddHook(Plugin, cPluginManager.E_PLUGIN_CHAT) -- used in web_chat.lua + PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHAT) -- used in web_chat.lua Plugin:AddCommand("/help", " - [Page] Show this message", "core.help") Plugin:AddCommand("/pluginlist", " - Show list of plugins", "core.pluginlist") diff --git a/MCServer/Plugins/Core/motd.lua b/MCServer/Plugins/Core/motd.lua index 49cdcecad..69f3ee245 100644 --- a/MCServer/Plugins/Core/motd.lua +++ b/MCServer/Plugins/Core/motd.lua @@ -5,6 +5,6 @@ end function ShowMOTDTo( Player ) Player:SendMessage( cChatColor.Gold .. "Welcome to the MCServer test server!" ); - Player:SendMessage( cChatColor.Gold .. "http://mcserver.ae-c.net/" ); + Player:SendMessage( cChatColor.Gold .. "http://www.mcserver.org/" ); Player:SendMessage( cChatColor.Gold .. "Type /help for all commands" ); end \ No newline at end of file diff --git a/MCServer/Plugins/Core/web_playerlist.lua b/MCServer/Plugins/Core/web_playerlist.lua index b7e48cc3f..c042c0072 100644 --- a/MCServer/Plugins/Core/web_playerlist.lua +++ b/MCServer/Plugins/Core/web_playerlist.lua @@ -4,12 +4,14 @@ function HandleRequest_PlayerList( Request ) if( Request.Params["playerlist-kick"] ~= nil ) then local KickPlayerName = Request.Params["playerlist-kick"] - local Player = World:GetPlayer( KickPlayerName ) - if( Player == nil ) then + local FoundPlayerCallback = function( Player ) + if( Player:GetName() == KickPlayerName ) then + Player:GetClientHandle():Kick("You were kicked from the game!") + Content = Content .. "

" .. KickPlayerName .. " has been kicked from the game!

" + end + end + if( World:DoWithPlayer( KickPlayerName, FoundPlayerCallback ) == false ) then Content = Content .. "

Could not find player " .. KickPlayerName .. " !

" - elseif( Player:GetName() == KickPlayerName ) then - Player:GetClientHandle():Kick("You were kicked from the game!") - Content = Content .. "

" .. KickPlayerName .. " has been kicked from the game!

" end end @@ -25,7 +27,7 @@ function HandleRequest_PlayerList( Request ) Content = Content .. "Kick" Content = Content .. "" end - World:ForEachPlayer( AddPlayerToTable ) + cRoot:Get():ForEachPlayer( AddPlayerToTable ) if( PlayerNum == 0 ) then Content = Content .. "None" diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp index 10109c669..f25a56aa2 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -108,91 +108,93 @@ static int tolua_LOGERROR(lua_State* tolua_S) -static int tolua_cWorld_DoWithPlayer(lua_State * tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 2) && (NumArgs != 3)) - { - LOGWARN("Error in function call 'cWorld:DoWithPlayer()': Requires 2 or 3 arguments, got %i", NumArgs ); - return 0; - } - - cWorld * self = (cWorld *) tolua_tousertype(tolua_S, 1, 0); - - const char * PlayerName = tolua_tocppstring(tolua_S, 2, ""); - if ((PlayerName == NULL) || (PlayerName[0] == 0)) - { - LOGWARN("Error in function call 'cWorld:DoWithPlayer()': Expected a non-empty string for parameter #1"); - return 0; - } - if (!lua_isfunction( tolua_S, 3)) - { - LOGWARN("Error in function call 'cWorld:DoWithPlayer()': Expected a function for parameter #2"); - return 0; - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 3) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - LOGWARN("Error in function call 'cWorld:DoWithPlayer()': Could not get value reference of parameter #3"); - return 0; - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - LOGWARN("Error in function call 'cWorld:DoWithPlayer()': Could not get function reference of parameter #2"); - return 0; - } - - class cLuaCallback : public cItemCallback - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState ) - , FuncRef( a_FuncRef ) - , TableRef( a_TableRef ) - {} - - private: - virtual bool Item(cPlayer * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, "cPlayer"); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - report_errors(LuaState, s); - return true; - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = self->DoWithPlayer(PlayerName, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal ); - return 1; +#define DEFINE_LUA_DOWITH(CONTAINER,ITEM,FOREACH,FNNAME) \ +static int FNNAME(lua_State * tolua_S) \ +{ \ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ \ + if ((NumArgs != 2) && (NumArgs != 3)) \ + { \ + LOGWARN("Error in function call '" #FOREACH "': Requires 2 or 3 arguments, got %i", NumArgs ); \ + return 0; \ + } \ + \ + CONTAINER * self = (CONTAINER *) tolua_tousertype(tolua_S, 1, 0); \ + \ + const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); \ + if ((ItemName == NULL) || (ItemName[0] == 0)) \ + { \ + LOGWARN("Error in function call '" #FOREACH "': Expected a non-empty string for parameter #1"); \ + return 0; \ + } \ + if (!lua_isfunction( tolua_S, 3)) \ + { \ + LOGWARN("Error in function call '" #FOREACH "': Expected a function for parameter #2"); \ + return 0; \ + } \ + \ + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ \ + int TableRef = LUA_REFNIL; \ + if (NumArgs == 3) \ + { \ + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); \ + if (TableRef == LUA_REFNIL) \ + { \ + LOGWARN("Error in function call '" #FOREACH "': Could not get value reference of parameter #3"); \ + return 0; \ + } \ + } \ + \ + /* table value is popped, and now function is on top of the stack */ \ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); \ + if (FuncRef == LUA_REFNIL) \ + { \ + LOGWARN("Error in function call '" #FOREACH "': Could not get function reference of parameter #2"); \ + return 0; \ + } \ + \ + class cLuaCallback : public cItemCallback \ + { \ + public: \ + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) \ + : LuaState( a_LuaState ) \ + , FuncRef( a_FuncRef ) \ + , TableRef( a_TableRef ) \ + {} \ + \ + private: \ + virtual bool Item(ITEM * a_Item) override \ + { \ + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ \ + tolua_pushusertype(LuaState, a_Item, #ITEM); \ + if (TableRef != LUA_REFNIL) \ + { \ + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ \ + } \ + \ + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); \ + report_errors(LuaState, s); \ + return true; \ + } \ + lua_State * LuaState; \ + int FuncRef; \ + int TableRef; \ + } Callback(tolua_S, FuncRef, TableRef); \ + \ + bool bRetVal = self->FOREACH(ItemName, Callback); \ + \ + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ \ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); \ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); \ + \ + /* Push return value on stack */ \ + tolua_pushboolean(tolua_S, bRetVal ); \ + return 1; \ } + #define DEFINE_LUA_FOREACHINCHUNK(CONTAINER,ITEM,FOREACH,FNNAME) \ static int FNNAME(lua_State * tolua_S) \ { \ @@ -378,10 +380,17 @@ static int FNNAME(lua_State * tolua_S) \ + +// Define the DoWith enumerators: (they take a string and a callback class) +DEFINE_LUA_DOWITH(cWorld, cPlayer, DoWithPlayer, tolua_cWorld_DoWithPlayer); +DEFINE_LUA_DOWITH(cWorld, cPlayer, FindAndDoWithPlayer, tolua_cWorld_FindAndDoWithPlayer); +DEFINE_LUA_DOWITH(cRoot, cPlayer, FindAndDoWithPlayer, tolua_cRoot_FindAndDoWithPlayer); + // Define the ForEach enumerators: DEFINE_LUA_FOREACH(cWorld, cPlayer, ForEachPlayer, tolua_cWorld_ForEachPlayer); -DEFINE_LUA_FOREACH(cRoot, cWorld, ForEachWorld, tolua_cRoot_ForEachWorld); DEFINE_LUA_FOREACH(cWorld, cEntity, ForEachEntity, tolua_cWorld_ForEachEntity); +DEFINE_LUA_FOREACH(cRoot, cWorld, ForEachWorld, tolua_cRoot_ForEachWorld); +DEFINE_LUA_FOREACH(cRoot, cPlayer, ForEachPlayer, tolua_cRoot_ForEachPlayer); DEFINE_LUA_FOREACHINCHUNK(cWorld, cEntity, ForEachEntityInChunk, tolua_cWorld_ForEachEntityInChunk); DEFINE_LUA_FOREACHINCHUNK(cWorld, cChestEntity, ForEachChestInChunk, tolua_cWorld_ForEachChestInChunk); @@ -694,6 +703,8 @@ void ManualBindings::Bind( lua_State* tolua_S ) tolua_beginmodule(tolua_S, "cRoot"); tolua_function(tolua_S, "ForEachWorld", tolua_cRoot_ForEachWorld); + tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_cRoot_FindAndDoWithPlayer); + tolua_function(tolua_S, "ForEachPlayer", tolua_cRoot_ForEachPlayer); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWorld"); @@ -702,7 +713,8 @@ void ManualBindings::Bind( lua_State* tolua_S ) tolua_function(tolua_S, "ForEachEntityInChunk", tolua_cWorld_ForEachEntityInChunk); tolua_function(tolua_S, "ForEachChestInChunk", tolua_cWorld_ForEachChestInChunk); tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_cWorld_ForEachFurnaceInChunk); - tolua_function(tolua_S, "DoWithPlayer", tolua_cWorld_DoWithPlayer); + tolua_function(tolua_S, "DoWithPlayer", tolua_cWorld_DoWithPlayer); + tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_cWorld_FindAndDoWithPlayer); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPlugin"); diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp index ccdbe687a..e3eb95a6e 100644 --- a/source/StringUtils.cpp +++ b/source/StringUtils.cpp @@ -172,6 +172,33 @@ int NoCaseCompare(const AString & s1, const AString & s2) +unsigned int RateCompareString(const AString & s1, const AString & s2 ) +{ + unsigned int MatchedLetters = 0; + unsigned int s1Length = s1.length(); + + if( s1Length > s2.length() ) return 0; // Definitely not a match + + for (unsigned int i = 0; i < s1Length; i++) + { + char c1 = (char)toupper( s1[i] ); + char c2 = (char)toupper( s2[i] ); + if( c1 == c2 ) + { + ++MatchedLetters; + } + else + { + break; + } + } + return MatchedLetters; +} + + + + + void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith) { size_t pos1 = iHayStack.find(iNeedle); diff --git a/source/StringUtils.h b/source/StringUtils.h index bc3c937b5..6ab95880a 100644 --- a/source/StringUtils.h +++ b/source/StringUtils.h @@ -42,6 +42,9 @@ extern AString & StrToUpper(AString & s); /// Case-insensitive string comparison; returns 0 if the strings are the same extern int NoCaseCompare(const AString & s1, const AString & s2); +/// Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()] +extern unsigned int RateCompareString(const AString & s1, const AString & s2 ); + /// Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith); diff --git a/source/cRoot.cpp b/source/cRoot.cpp index 5c0c08f7d..4c87210c7 100644 --- a/source/cRoot.cpp +++ b/source/cRoot.cpp @@ -14,6 +14,7 @@ #include "cThread.h" #include "cFileFormatUpdater.h" #include "cRedstone.h" +#include "cPlayer.h" #include "blocks/Block.h" #include "items/Item.h" #include "cChunk.h" @@ -423,6 +424,58 @@ bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback) +bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + class cCallback : public cPlayerListCallback + { + unsigned int BestRating; + unsigned int NameLength; + const AString PlayerName; + + cPlayerListCallback & m_Callback; + virtual bool Item (cPlayer * a_pPlayer) + { + unsigned int Rating = RateCompareString (PlayerName, a_pPlayer->GetName()); + if (Rating > 0 && Rating >= BestRating) + { + BestMatch = a_pPlayer; + if( Rating > BestRating ) NumMatches = 0; + BestRating = Rating; + ++NumMatches; + } + if (Rating == NameLength) // Perfect match + { + return false; + } + return true; + } + + public: + cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) + : m_Callback( a_Callback ) + , BestMatch( NULL ) + , BestRating( 0 ) + , NumMatches( 0 ) + , NameLength( a_PlayerName.length() ) + , PlayerName( a_PlayerName ) + {} + + cPlayer * BestMatch; + unsigned int NumMatches; + } Callback (a_PlayerName, a_Callback); + ForEachPlayer( Callback ); + + if (Callback.NumMatches == 1) + { + return a_Callback.Item (Callback.BestMatch); + } + return false; +} + + + + + void cRoot::LogChunkStats(void) { int SumNumValid = 0; diff --git a/source/cRoot.h b/source/cRoot.h index b65c1d91d..e25a9805b 100644 --- a/source/cRoot.h +++ b/source/cRoot.h @@ -70,7 +70,10 @@ public: void SaveAllChunks(void); /// Calls the callback for each player in all worlds - bool ForEachPlayer(cPlayerListCallback & a_Callback); + bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Finds a player from a partial or complete player name and calls the callback - case-insensitive + bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << private: void LoadGlobalSettings(); diff --git a/source/cWorld.cpp b/source/cWorld.cpp index a981a24c0..f12f9f1a8 100644 --- a/source/cWorld.cpp +++ b/source/cWorld.cpp @@ -1614,6 +1614,42 @@ bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_ +bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + cPlayer* BestMatch = 0; + unsigned int BestRating = 0; + unsigned int NumMatches = 0; + unsigned int NameLength = a_PlayerName.length(); + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + unsigned int Rating = RateCompareString (a_PlayerName, (*itr)->GetName()); + if (Rating > 0 && Rating >= BestRating) + { + BestMatch = *itr; + if( Rating > BestRating ) NumMatches = 0; + BestRating = Rating; + ++NumMatches; + } + if (Rating == NameLength) // Perfect match + { + break; + } + } // for itr - m_Players[] + + if (NumMatches == 1) + { + LOG("Compared %s and %s with rating %i", a_PlayerName.c_str(), BestMatch->GetName().c_str(), BestRating ); + return a_Callback.Item (BestMatch); + } + return false; +} + + + + + // TODO: This interface is dangerous! cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) { diff --git a/source/cWorld.h b/source/cWorld.h index 5c2132050..6cac25b66 100644 --- a/source/cWorld.h +++ b/source/cWorld.h @@ -150,6 +150,9 @@ public: /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Finds a player from a partial or complete player name and calls the callback - case-insensitive + bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << unsigned int GetNumPlayers(); //tolua_export