1
0

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
This commit is contained in:
faketruth 2012-08-22 23:05:12 +00:00
parent 886c7f5624
commit 49a4613d94
11 changed files with 245 additions and 104 deletions

View File

@ -4,24 +4,26 @@ function HandleKickCommand( Split, Player )
return true return true
end end
local World = Player:GetWorld() local FoundPlayerCallback = function( OtherPlayer )
local OtherPlayer = World:GetPlayer( Split[2] ) local Reason = "You have been kicked"
if( OtherPlayer == nil ) then 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 )
end
if( cRoot:Get():FindAndDoWithPlayer( Split[2], FoundPlayerCallback ) == false ) then
Player:SendMessage( cChatColor.Green .. "Could not find player " .. Split[2] ) Player:SendMessage( cChatColor.Green .. "Could not find player " .. Split[2] )
return true return true
end 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 return true
end end

View File

@ -22,7 +22,7 @@ function Initialize( Plugin )
PluginManager:AddHook(Plugin, cPluginManager.HOOK_BLOCK_DIG) PluginManager:AddHook(Plugin, cPluginManager.HOOK_BLOCK_DIG)
PluginManager:AddHook(Plugin, cPluginManager.HOOK_KILLED) PluginManager:AddHook(Plugin, cPluginManager.HOOK_KILLED)
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CRAFTING_NO_RECIPE) 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("/help", " - [Page] Show this message", "core.help")
Plugin:AddCommand("/pluginlist", " - Show list of plugins", "core.pluginlist") Plugin:AddCommand("/pluginlist", " - Show list of plugins", "core.pluginlist")

View File

@ -5,6 +5,6 @@ end
function ShowMOTDTo( Player ) function ShowMOTDTo( Player )
Player:SendMessage( cChatColor.Gold .. "Welcome to the MCServer test server!" ); 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" ); Player:SendMessage( cChatColor.Gold .. "Type /help for all commands" );
end end

View File

@ -4,12 +4,14 @@ function HandleRequest_PlayerList( Request )
if( Request.Params["playerlist-kick"] ~= nil ) then if( Request.Params["playerlist-kick"] ~= nil ) then
local KickPlayerName = Request.Params["playerlist-kick"] local KickPlayerName = Request.Params["playerlist-kick"]
local Player = World:GetPlayer( KickPlayerName ) local FoundPlayerCallback = function( Player )
if( Player == nil ) then if( Player:GetName() == KickPlayerName ) then
Player:GetClientHandle():Kick("You were kicked from the game!")
Content = Content .. "<p>" .. KickPlayerName .. " has been kicked from the game!</p>"
end
end
if( World:DoWithPlayer( KickPlayerName, FoundPlayerCallback ) == false ) then
Content = Content .. "<p>Could not find player " .. KickPlayerName .. " !</p>" Content = Content .. "<p>Could not find player " .. KickPlayerName .. " !</p>"
elseif( Player:GetName() == KickPlayerName ) then
Player:GetClientHandle():Kick("You were kicked from the game!")
Content = Content .. "<p>" .. KickPlayerName .. " has been kicked from the game!</p>"
end end
end end
@ -25,7 +27,7 @@ function HandleRequest_PlayerList( Request )
Content = Content .. "<td><a href='?playerlist-kick=" .. Player:GetName() .. "'>Kick</a></td>" Content = Content .. "<td><a href='?playerlist-kick=" .. Player:GetName() .. "'>Kick</a></td>"
Content = Content .. "</tr>" Content = Content .. "</tr>"
end end
World:ForEachPlayer( AddPlayerToTable ) cRoot:Get():ForEachPlayer( AddPlayerToTable )
if( PlayerNum == 0 ) then if( PlayerNum == 0 ) then
Content = Content .. "<tr><td>None</td></tr>" Content = Content .. "<tr><td>None</td></tr>"

View File

@ -108,91 +108,93 @@ static int tolua_LOGERROR(lua_State* tolua_S)
static int tolua_cWorld_DoWithPlayer(lua_State * tolua_S) #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)) 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; LOGWARN("Error in function call '" #FOREACH "': Requires 2 or 3 arguments, got %i", NumArgs ); \
} return 0; \
} \
cWorld * self = (cWorld *) tolua_tousertype(tolua_S, 1, 0); \
CONTAINER * self = (CONTAINER *) tolua_tousertype(tolua_S, 1, 0); \
const char * PlayerName = tolua_tocppstring(tolua_S, 2, ""); \
if ((PlayerName == NULL) || (PlayerName[0] == 0)) const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); \
{ if ((ItemName == NULL) || (ItemName[0] == 0)) \
LOGWARN("Error in function call 'cWorld:DoWithPlayer()': Expected a non-empty string for parameter #1"); { \
return 0; LOGWARN("Error in function call '" #FOREACH "': Expected a non-empty string for parameter #1"); \
} return 0; \
if (!lua_isfunction( tolua_S, 3)) } \
{ if (!lua_isfunction( tolua_S, 3)) \
LOGWARN("Error in function call 'cWorld:DoWithPlayer()': Expected a function for parameter #2"); { \
return 0; 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; /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ \
if (NumArgs == 3) int TableRef = LUA_REFNIL; \
{ if (NumArgs == 3) \
TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); { \
if (TableRef == LUA_REFNIL) 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; 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); /* table value is popped, and now function is on top of the stack */ \
if (FuncRef == LUA_REFNIL) 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; LOGWARN("Error in function call '" #FOREACH "': Could not get function reference of parameter #2"); \
} return 0; \
} \
class cLuaCallback : public cItemCallback<cPlayer> \
{ class cLuaCallback : public cItemCallback<ITEM> \
public: { \
cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) public: \
: LuaState( a_LuaState ) cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) \
, FuncRef( a_FuncRef ) : LuaState( a_LuaState ) \
, TableRef( a_TableRef ) , FuncRef( a_FuncRef ) \
{} , TableRef( a_TableRef ) \
{} \
private: \
virtual bool Item(cPlayer * a_Item) override private: \
{ virtual bool Item(ITEM * a_Item) override \
lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ { \
tolua_pushusertype(LuaState, a_Item, "cPlayer"); lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ \
if (TableRef != LUA_REFNIL) tolua_pushusertype(LuaState, a_Item, #ITEM); \
{ if (TableRef != LUA_REFNIL) \
lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ { \
} 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); int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); \
return true; report_errors(LuaState, s); \
} return true; \
lua_State * LuaState; } \
int FuncRef; lua_State * LuaState; \
int TableRef; int FuncRef; \
} Callback(tolua_S, FuncRef, TableRef); int TableRef; \
} Callback(tolua_S, FuncRef, TableRef); \
bool bRetVal = self->DoWithPlayer(PlayerName, Callback); \
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); /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ \
luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); \
luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); \
/* Push return value on stack */ \
tolua_pushboolean(tolua_S, bRetVal ); /* Push return value on stack */ \
return 1; tolua_pushboolean(tolua_S, bRetVal ); \
return 1; \
} }
#define DEFINE_LUA_FOREACHINCHUNK(CONTAINER,ITEM,FOREACH,FNNAME) \ #define DEFINE_LUA_FOREACHINCHUNK(CONTAINER,ITEM,FOREACH,FNNAME) \
static int FNNAME(lua_State * tolua_S) \ 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 the ForEach enumerators:
DEFINE_LUA_FOREACH(cWorld, cPlayer, ForEachPlayer, tolua_cWorld_ForEachPlayer); 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(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, cEntity, ForEachEntityInChunk, tolua_cWorld_ForEachEntityInChunk);
DEFINE_LUA_FOREACHINCHUNK(cWorld, cChestEntity, ForEachChestInChunk, tolua_cWorld_ForEachChestInChunk); 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_beginmodule(tolua_S, "cRoot");
tolua_function(tolua_S, "ForEachWorld", tolua_cRoot_ForEachWorld); 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_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cWorld"); 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, "ForEachEntityInChunk", tolua_cWorld_ForEachEntityInChunk);
tolua_function(tolua_S, "ForEachChestInChunk", tolua_cWorld_ForEachChestInChunk); tolua_function(tolua_S, "ForEachChestInChunk", tolua_cWorld_ForEachChestInChunk);
tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_cWorld_ForEachFurnaceInChunk); 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_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cPlugin"); tolua_beginmodule(tolua_S, "cPlugin");

View File

@ -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) void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith)
{ {
size_t pos1 = iHayStack.find(iNeedle); size_t pos1 = iHayStack.find(iNeedle);

View File

@ -42,6 +42,9 @@ extern AString & StrToUpper(AString & s);
/// Case-insensitive string comparison; returns 0 if the strings are the same /// Case-insensitive string comparison; returns 0 if the strings are the same
extern int NoCaseCompare(const AString & s1, const AString & s2); 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 /// Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith
extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith); extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith);

View File

@ -14,6 +14,7 @@
#include "cThread.h" #include "cThread.h"
#include "cFileFormatUpdater.h" #include "cFileFormatUpdater.h"
#include "cRedstone.h" #include "cRedstone.h"
#include "cPlayer.h"
#include "blocks/Block.h" #include "blocks/Block.h"
#include "items/Item.h" #include "items/Item.h"
#include "cChunk.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) void cRoot::LogChunkStats(void)
{ {
int SumNumValid = 0; int SumNumValid = 0;

View File

@ -70,7 +70,10 @@ public:
void SaveAllChunks(void); void SaveAllChunks(void);
/// Calls the callback for each player in all worlds /// 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: private:
void LoadGlobalSettings(); void LoadGlobalSettings();

View File

@ -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! // TODO: This interface is dangerous!
cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit)
{ {

View File

@ -151,6 +151,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 /// 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 << 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 unsigned int GetNumPlayers(); //tolua_export
// TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action) // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)