1
0

MCServer c++ source files

git-svn-id: http://mc-server.googlecode.com/svn/trunk@3 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
faketruth 2011-10-03 18:41:19 +00:00
parent cc2b15a233
commit 386d58b586
233 changed files with 35759 additions and 0 deletions

3
source/AllToLua.bat Normal file
View File

@ -0,0 +1,3 @@
"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
PAUSE
echo "tolua++.exe" -o Bindings.cpp -H Bindings.h AllToLua.pkg

41
source/AllToLua.pkg Normal file
View File

@ -0,0 +1,41 @@
$#include "tolua_base.h"
$cfile "cTorch.h"
$cfile "cStairs.h"
$cfile "cLadder.h"
$cfile "../iniFile/iniFile.h"
$cfile "BlockID.h"
$cfile "PacketID.h"
$cfile "Defines.h"
$cfile "LuaFunctions.h"
$cfile "cStringMap.h"
$cfile "cChatColor.h"
$cfile "cClientHandle.h"
$cfile "cEntity.h"
$cfile "cPawn.h"
$cfile "cPlayer.h"
$cfile "cPluginManager.h"
$cfile "cPlugin.h"
$cfile "cPlugin_Lua.h"
$cfile "cServer.h"
$cfile "cWorld.h"
$cfile "cInventory.h"
$cfile "cItem.h"
$cfile "cWebAdmin.h"
$cfile "cWebPlugin.h"
$cfile "cPickup.h"
$cfile "cRoot.h"
$cfile "cTCPLink.h"
$cfile "Vector3f.h"
$cfile "Vector3d.h"
$cfile "Vector3i.h"
$cfile "Matrix4f.h"
$cfile "cCuboid.h"
$cfile "cMCLogger.h"
$cfile "cTracer.h"
$cfile "cGroup.h"
$cfile "packets/cPacket_Login.h"
$cfile "packets/cPacket_BlockDig.h"
$cfile "packets/cPacket_BlockPlace.h"

15468
source/Bindings.cpp Normal file

File diff suppressed because it is too large Load Diff

8
source/Bindings.h Normal file
View File

@ -0,0 +1,8 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 07/15/11 01:36:28.
*/
/* Exported function */
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);

151
source/BlockID.h Normal file
View File

@ -0,0 +1,151 @@
#pragma once
//tolua_begin
enum ENUM_BLOCK_ID
{
E_BLOCK_AIR = 0,
E_BLOCK_STONE = 1,
E_BLOCK_GRASS = 2,
E_BLOCK_DIRT = 3,
E_BLOCK_COBBLESTONE = 4,
E_BLOCK_WOOD = 5,
E_BLOCK_SAPLING = 6,
E_BLOCK_BEDROCK = 7,
E_BLOCK_WATER = 8,
E_BLOCK_STATIONARY_WATER = 9,
E_BLOCK_LAVA = 10,
E_BLOCK_STATIONARY_LAVA = 11,
E_BLOCK_SAND = 12,
E_BLOCK_GRAVEL = 13,
E_BLOCK_GOLD_ORE = 14,
E_BLOCK_IRON_ORE = 15,
E_BLOCK_COAL_ORE = 16,
E_BLOCK_LOG = 17,
E_BLOCK_LEAVES = 18,
E_BLOCK_SPONGE = 19,
E_BLOCK_GLASS = 20,
E_BLOCK_WHITE_CLOTH = 35,
E_BLOCK_YELLOW_FLOWER = 37,
E_BLOCK_RED_ROSE = 38,
E_BLOCK_BROWN_MUSHROOM = 39,
E_BLOCK_RED_MUSHROOM = 40,
E_BLOCK_GOLD_BLOCK = 41,
E_BLOCK_IRON_BLOCK = 42,
E_BLOCK_DOUBLE_STEP = 43,
E_BLOCK_STEP = 44,
E_BLOCK_BRICK = 45,
E_BLOCK_TNT = 46,
E_BLOCK_BOOKCASE = 47,
E_BLOCK_MOSSY_COBBLESTONE = 48,
E_BLOCK_OBSIDIAN = 49,
E_BLOCK_TORCH = 50,
E_BLOCK_FIRE = 51,
E_BLOCK_MOB_SPAWNER = 52,
E_BLOCK_WOODEN_STAIRS = 53,
E_BLOCK_CHEST = 54,
E_BLOCK_REDSTONE_WIRE = 55,
E_BLOCK_DIAMOND_ORE = 56,
E_BLOCK_DIAMOND_BLOCK = 57,
E_BLOCK_WORKBENCH = 58,
E_BLOCK_CROPS = 59,
E_BLOCK_SOIL = 60,
E_BLOCK_FURNACE = 61,
E_BLOCK_BURNING_FURNACE = 62,
E_BLOCK_SIGN_POST = 63,
E_BLOCK_WOODEN_DOOR = 64,
E_BLOCK_LADDER = 65,
E_BLOCK_MINECART_TRACKS = 66,
E_BLOCK_COBBLESTONE_STAIRS = 67,
E_BLOCK_WALLSIGN = 68,
E_BLOCK_LEVER = 69,
E_BLOCK_STONE_PRESSURE_PLATE = 70,
E_BLOCK_IRON_DOOR = 71,
E_BLOCK_WOODEN_PRESSURE_PLATE = 72,
E_BLOCK_REDSTONE_ORE = 73,
E_BLOCK_REDSTONE_ORE_GLOWING = 74,
E_BLOCK_REDSTONE_TORCH_ON = 75,
E_BLOCK_REDSTONE_TORCH_OFF = 76,
E_BLOCK_SNOW = 78,
E_BLOCK_ICE = 79,
E_BLOCK_SNOW_BLOCK = 80,
E_BLOCK_CACTUS = 81,
E_BLOCK_CLAY = 82,
E_BLOCK_REEDS = 83,
E_BLOCK_BLOODSTONE = 87,
E_BLOCK_SOULSAND = 88,
E_BLOCK_GLOWSTONE = 89,
E_BLOCK_PORT = 90,
E_BLOCK_JACK_O_LANTERN = 91,
E_BLOCK_CAKE = 92,
E_BLOCK_REDSTONE_REPEATER_OFF = 93,
E_BLOCK_REDSTONE_REPEATER_ON = 94,
E_BLOCK_LOCKED_CHEST = 95,
E_BLOCK_TRAPDOOR = 96,
};
//tolua_end
//tolua_begin
enum ENUM_ITEM_ID
{
E_ITEM_EMPTY = -1,
E_ITEM_STONE = 1,
E_ITEM_GRASS = 2,
E_ITEM_DIRT = 3,
E_ITEM_COBBLESTONE = 4,
E_ITEM_WOOD = 5,
E_ITEM_SAPLING = 6,
E_ITEM_BEDROCK = 7,
E_ITEM_WATER = 8,
E_ITEM_STATIONARY_WATER = 9,
E_ITEM_LAVA = 10,
E_ITEM_STATIONARY_LAVA = 11,
E_ITEM_SAND = 12,
E_ITEM_GRAVEL = 13,
E_ITEM_GOLD_ORE = 14,
E_ITEM_IRON_ORE = 15,
E_ITEM_COAL_ORE = 16,
E_ITEM_LOG = 17,
E_ITEM_LEAVES = 18,
E_ITEM_GOLD_BLOCK = 41,
E_ITEM_IRON_BLOCK = 42,
E_ITEM_DIAMOND_BLOCK = 57,
E_ITEM_FLINT_AND_STEEL = 259,
E_ITEM_APPLE = 260,
E_ITEM_BOW = 261,
E_ITEM_ARROW = 262,
E_ITEM_COAL = 263,
E_ITEM_DIAMOND = 264,
E_ITEM_IRON = 265,
E_ITEM_GOLD = 266,
E_ITEM_MUSHROOM_SOUP = 282,
E_ITEM_GOLD_SWORD = 283,
E_ITEM_GOLD_SHOVEL = 284,
E_ITEM_GOLD_PICKAXE = 285,
E_ITEM_GOLD_AXE = 286,
E_ITEM_STRING = 287,
E_ITEM_FEATHER = 288,
E_ITEM_BREAD = 297,
E_ITEM_RAW_MEAT = 319,
E_ITEM_COOKED_MEAT = 320,
E_ITEM_GOLDEN_APPLE = 322,
E_ITEM_SIGN = 323,
E_ITEM_MILK = 335,
E_ITEM_EGG = 344,
E_ITEM_COMPASS = 345,
E_ITEM_FISHING_ROD = 346,
E_ITEM_CLOCK = 347,
E_ITEM_GLOWSTONE_DUST = 348,
E_ITEM_RAW_FISH = 349,
E_ITEM_COOKED_FISH = 350,
E_ITEM_CAKE = 354,
};
//tolua_end

129
source/Defines.h Normal file
View File

@ -0,0 +1,129 @@
#pragma once
#include "MemoryLeak.h"
//tolua_begin
// emissive blocks
extern char g_BlockLightValue[];
// whether blocks allow spreading
extern char g_BlockSpreadLightFalloff[];
// whether blocks are transparent (light can shine though)
extern bool g_BlockTransparent[];
// one hit break blocks
extern bool g_BlockOneHitDig[];
//tolua_end
//--DO NOT DELETE THIS COMMENT-- //tolua_export
inline bool IsValidBlock( int a_BlockID ) //tolua_export
{ //tolua_export
if( a_BlockID > -1 &&
a_BlockID <= 96 &&
a_BlockID != 29 &&
a_BlockID != 33 &&
a_BlockID != 34 &&
a_BlockID != 36 )
{
return true;
}
return false;
} //tolua_export
inline bool isValidItem( int a_ItemID ) //tolua_export
{ //tolua_export
if( (a_ItemID >= 256 && a_ItemID <= 358)
|| (a_ItemID == 2256 || a_ItemID == 2257) )
{
return true;
}
if( a_ItemID == 0 )
return false;
return IsValidBlock( a_ItemID );
} //tolua_export
inline void AddDirection( int & a_X, char & a_Y, int & a_Z, char a_Direction, bool a_bInverse = false ) //tolua_export
{//tolua_export
if( !a_bInverse )
{
switch( a_Direction )
{
case 0:
a_Y--;
break;
case 1:
a_Y++;
break;
case 2:
a_Z--;
break;
case 3:
a_Z++;
break;
case 4:
a_X--;
break;
case 5:
a_X++;
break;
};
}
else
{
switch( a_Direction ) // other way around
{
case 0:
a_Y++;
break;
case 1:
a_Y--;
break;
case 2:
a_Z++;
break;
case 3:
a_Z--;
break;
case 4:
a_X++;
break;
case 5:
a_X--;
break;
};
}
}//tolua_export
#include <math.h>
#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)>(b))?(a):(b))
inline void EulerToVector( float a_Pan, float a_Pitch, float & a_X, float & a_Y, float & a_Z )
{
// a_X = sinf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI );
// a_Y = -sinf ( a_Pitch / 180 * PI );
// a_Z = -cosf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI );
a_X = cos(a_Pan / 180 * PI)*cos(a_Pitch / 180 * PI);
a_Y = sin(a_Pan / 180 * PI)*cos(a_Pitch / 180 * PI);
a_Z = sin(a_Pitch / 180 * PI);
}
inline void VectorToEuler( float a_X, float a_Y, float a_Z, float & a_Pan, float & a_Pitch )
{
if( a_X != 0 )
a_Pan = atan2( a_Z, a_X ) * 180 / PI - 90;
else
a_Pan = 0;
a_Pitch = atan2(a_Y, sqrtf((a_X * a_X) + (a_Z * a_Z))) * 180 / PI;
}
inline float GetSignf( float a_Val )
{
return (a_Val < 0.f)?-1.f:1.f;
}
inline float GetSpecialSignf( float a_Val )
{
return (a_Val <= 0.f)?-1.f:1.f;
}

51
source/Endianness.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <string>
#ifdef _WIN32
#include <WinSock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#endif
// Changes endianness
inline unsigned long long HostToNetwork8( void* a_Value )
{
unsigned long long __HostToNetwork8;
memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8 ) );
__HostToNetwork8 = (( ( (unsigned long long)htonl((u_long)__HostToNetwork8) ) << 32) + htonl(__HostToNetwork8 >> 32));
return __HostToNetwork8;
}
inline unsigned int HostToNetwork4( void* a_Value )
{
unsigned int __HostToNetwork4;
memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4 ) );
__HostToNetwork4 = ntohl( __HostToNetwork4 );
return __HostToNetwork4;
}
inline double NetworkToHostDouble8( void* a_Value )
{
#define ntohll(x) ((((unsigned long long)ntohl((u_long)x)) << 32) + ntohl(x >> 32))
unsigned long long buf = 0;//(*(unsigned long long*)a_Value);
memcpy( &buf, a_Value, 8 );
buf = ntohll(buf);
double x;
memcpy(&x, &buf, sizeof(double));
return x;
}
inline long long NetworkToHostLong8( void* a_Value )
{
unsigned long long buf = *(unsigned long long*)a_Value;
buf = ntohll(buf);
return *reinterpret_cast<long long *>(&buf);
}
inline float NetworkToHostFloat4( void* a_Value )
{
u_long buf = *(u_long*)a_Value;
buf = ntohl( buf );
return *(float*)reinterpret_cast<float *>(&buf);
}

21
source/FileDefine.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
// So we don't have to include fstream :P
#ifdef _WIN32
#ifndef _FILE_DEFINED
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif
#else
#include <stdio.h>
#endif

17
source/LuaFunctions.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "cMCLogger.h"
#include <time.h>
// tolua_begin
unsigned int GetTime()
{
return (unsigned int)time(0);
}
std::string GetChar( std::string & a_Str, unsigned int a_Idx )
{
return std::string(1, a_Str[ a_Idx ]);
}
// tolua_end

41
source/MCSocket.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#ifdef _WIN32
#include <winsock2.h>
#define socklen_t int
#ifdef SendMessage
#undef SendMessage
#endif
#else
// Linux threads http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define SOCKET int
typedef void *HANDLE;
#define CRITICAL_SECTION pthread_mutex_t
#define SD_BOTH (2)
#define closesocket(x) (shutdown(x, SD_BOTH), close(x))
#define SOCKET_ERROR SO_ERROR
#define EnterCriticalSection(x) pthread_mutex_lock(x)
#define LeaveCriticalSection(x) pthread_mutex_unlock(x)
#define InitializeCriticalSection(x) pthread_mutex_init(x, NULL)
#define DeleteCriticalSection(x) (x)
#define sprintf_s(x, y, ...) sprintf(x, __VA_ARGS__)
#endif
inline bool IsSocketError( int a_ReturnedValue )
{
#ifdef _WIN32
return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0);
#else
return (a_ReturnedValue <= 0);
#endif
}

229
source/ManualBindings.cpp Normal file
View File

@ -0,0 +1,229 @@
#include "ManualBindings.h"
#include "tolua++.h"
#include "cMCLogger.h"
#include "cRoot.h"
#include "cWorld.h"
#include "cPlugin.h"
#include "cPluginManager.h"
#include "cLuaCommandBinder.h"
#include "cPlayer.h"
#include "md5/md5.h"
extern std::vector<std::string> StringSplit(std::string str, std::string delim);
/****************************
* Lua bound functions with special return types
**/
static int tolua_StringSplit(lua_State* tolua_S)
{
std::string str = ((std::string) tolua_tocppstring(tolua_S,1,0));
std::string delim = ((std::string) tolua_tocppstring(tolua_S,2,0));
std::vector<std::string> Split = StringSplit( str, delim );
lua_createtable(tolua_S, Split.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
std::vector<std::string>::const_iterator iter = Split.begin();
while(iter != Split.end()) {
tolua_pushstring( tolua_S, (*iter).c_str() );
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
}
return 1;
}
static int tolua_LOG(lua_State* tolua_S)
{
const char* str = tolua_tocppstring(tolua_S,1,0);
cMCLogger::GetInstance()->LogSimple( str, 0 );
return 0;
}
static int tolua_LOGINFO(lua_State* tolua_S)
{
const char* str = tolua_tocppstring(tolua_S,1,0);
cMCLogger::GetInstance()->LogSimple( str, 1 );
return 0;
}
static int tolua_LOGWARN(lua_State* tolua_S)
{
const char* str = tolua_tocppstring(tolua_S,1,0);
cMCLogger::GetInstance()->LogSimple( str, 2 );
return 0;
}
static int tolua_LOGERROR(lua_State* tolua_S)
{
const char* str = tolua_tocppstring(tolua_S,1,0);
cMCLogger::GetInstance()->LogSimple( str, 3 );
return 0;
}
static int tolua_cWorld_GetAllPlayers(lua_State* tolua_S)
{
cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
lua_State* L = tolua_S;
self->GetAllPlayers(L);
return 1;
}
static int tolua_cPlugin_GetCommands(lua_State* tolua_S)
{
cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0);
const std::vector< cPlugin::CommandStruct > & AllCommands = self->GetCommands();
lua_createtable(tolua_S, AllCommands.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
std::vector< cPlugin::CommandStruct >::const_iterator iter = AllCommands.begin();
while(iter != AllCommands.end())
{
const cPlugin::CommandStruct & CS = *iter;
tolua_pushusertype( tolua_S, (void*)&CS, "const cPlugin::CommandStruct" );
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
}
return 1;
}
static int tolua_cPluginManager_GetAllPlugins(lua_State* tolua_S)
{
cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
const cPluginManager::PluginList & AllPlugins = self->GetAllPlugins();
lua_createtable(tolua_S, AllPlugins.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
cPluginManager::PluginList::const_iterator iter = AllPlugins.begin();
while(iter != AllPlugins.end())
{
const cPlugin* Plugin = *iter;
tolua_pushusertype( tolua_S, (void*)Plugin, "const cPlugin" );
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
}
return 1;
}
static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
const cPlayer::GroupList & AllGroups = self->GetGroups();
lua_createtable(tolua_S, AllGroups.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
cPlayer::GroupList::const_iterator iter = AllGroups.begin();
while(iter != AllGroups.end())
{
const cGroup* Group = *iter;
tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" );
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
}
return 1;
}
static int tolua_cPlugin_BindCommand(lua_State* tolua_S)
{
cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0);
cPluginManager* PluginManager = cRoot::Get()->GetPluginManager();
cLuaCommandBinder* CommandBinder = PluginManager->GetLuaCommandBinder();
tolua_Error tolua_err;
tolua_err.array = 0;
tolua_err.index = 0;
tolua_err.type = 0;
std::string Permission = "";
std::string Command = "";
int Reference = LUA_REFNIL;
if( tolua_isstring( tolua_S, 2, 0, &tolua_err ) &&
lua_isfunction( tolua_S, 3 ) )
{
Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
Command = ((std::string) tolua_tocppstring(tolua_S,2,0));
}
else if( tolua_isstring( tolua_S, 2, 0, &tolua_err ) &&
tolua_isstring( tolua_S, 3, 0, &tolua_err ) &&
lua_isfunction( tolua_S, 4 ) )
{
Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
Command = ((std::string) tolua_tocppstring(tolua_S,2,0));
Permission = ((std::string) tolua_tocppstring(tolua_S,3,0));
}
else
{
if( tolua_err.type == 0 )
{
tolua_err.type = "function";
}
tolua_error(tolua_S,"#ferror in function 'BindCommand'.",&tolua_err);
return 0;
}
if( Reference != LUA_REFNIL )
{
if( !CommandBinder->BindCommand( Command, Permission, self, tolua_S, Reference ) )
{
luaL_unref( tolua_S, LUA_REGISTRYINDEX, Reference );
}
}
else
{
LOGERROR("ERROR: cPlugin:BindCommand invalid function reference in 2nd argument (Command: \"%s\")", Command.c_str() );
}
return 0;
}
static int tolua_md5(lua_State* tolua_S)
{
std::string SourceString = tolua_tostring(tolua_S, 1, 0);
std::string CryptedString = md5( SourceString );
tolua_pushstring( tolua_S, CryptedString.c_str() );
return 1;
}
void ManualBindings::Bind( lua_State* tolua_S )
{
tolua_beginmodule(tolua_S,NULL);
tolua_function(tolua_S,"StringSplit",tolua_StringSplit);
tolua_function(tolua_S,"LOG",tolua_LOG);
tolua_function(tolua_S,"LOGINFO",tolua_LOGINFO);
tolua_function(tolua_S,"LOGWARN",tolua_LOGWARN);
tolua_function(tolua_S,"LOGERROR",tolua_LOGERROR);
tolua_function(tolua_S,"Log",tolua_LOG); // Deprecated
tolua_beginmodule(tolua_S,"cWorld");
tolua_function(tolua_S,"GetAllPlayers",tolua_cWorld_GetAllPlayers);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S,"cPlugin");
tolua_function(tolua_S,"GetCommands",tolua_cPlugin_GetCommands);
tolua_function(tolua_S,"BindCommand",tolua_cPlugin_BindCommand);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S,"cPluginManager");
tolua_function(tolua_S,"GetAllPlugins",tolua_cPluginManager_GetAllPlugins);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S,"cPlayer");
tolua_function(tolua_S,"GetGroups",tolua_cPlayer_GetGroups);
tolua_endmodule(tolua_S);
tolua_function(tolua_S,"md5",tolua_md5);
tolua_endmodule(tolua_S);
}

8
source/ManualBindings.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
struct lua_State;
class ManualBindings
{
public:
static void Bind( lua_State* tolua_S );
};

0
source/Matrix4f.cpp Normal file
View File

111
source/Matrix4f.h Normal file
View File

@ -0,0 +1,111 @@
#pragma once
#define _USE_MATH_DEFINES
#include <math.h>
#include "Vector3f.h"
class Matrix4f
{
public:
enum
{
TX=3,
TY=7,
TZ=11,
D0=0, D1=5, D2=10, D3=15,
SX=D0, SY=D1, SZ=D2,
W=D3
};
Matrix4f() { Identity(); }
float& operator [] ( int a_N ) { return cell[a_N]; }
void Identity()
{
cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] =
cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0;
cell[D0] = cell[D1] = cell[D2] = cell[W] = 1;
}
void Init( Vector3f a_Pos, float a_RX, float a_RY, float a_RZ )
{
Matrix4f t;
t.RotateX( a_RZ );
RotateY( a_RY );
Concatenate( t );
t.RotateZ( a_RX );
Concatenate( t );
Translate( a_Pos );
}
void RotateX( float a_RX )
{
float sx = (float)sin( a_RX * M_PI / 180 );
float cx = (float)cos( a_RX * M_PI / 180 );
Identity();
cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx;
}
void RotateY( float a_RY )
{
float sy = (float)sin( a_RY * M_PI / 180 );
float cy = (float)cos( a_RY * M_PI / 180 );
Identity ();
cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy;
}
void RotateZ( float a_RZ )
{
float sz = (float)sin( a_RZ * M_PI / 180 );
float cz = (float)cos( a_RZ * M_PI / 180 );
Identity ();
cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz;
}
void Translate( Vector3f a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; }
void SetTranslation( Vector3f a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; }
void Concatenate( const Matrix4f& m2 )
{
Matrix4f res;
int c;
for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ )
res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] +
cell[r * 4 + 1] * m2.cell[c + 4] +
cell[r * 4 + 2] * m2.cell[c + 8] +
cell[r * 4 + 3] * m2.cell[c + 12];
for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c];
}
Vector3f Transform( const Vector3f& v ) const
{
float x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
float y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
float z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
return Vector3f( x, y, z );
}
void Invert()
{
Matrix4f t;
int h, i;
float tx = -cell[3], ty = -cell[7], tz = -cell[11];
for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4];
for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i];
cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
}
Vector3f GetXColumn() { return Vector3f( cell[0], cell[1], cell[2] ); }
Vector3f GetYColumn() { return Vector3f( cell[4], cell[5], cell[6] ); }
Vector3f GetZColumn() { return Vector3f( cell[8], cell[9], cell[10] ); }
void SetXColumn( const Vector3f & a_X )
{
cell[0] = a_X.x;
cell[1] = a_X.y;
cell[2] = a_X.z;
}
void SetYColumn( const Vector3f & a_Y )
{
cell[4] = a_Y.x;
cell[5] = a_Y.y;
cell[6] = a_Y.z;
}
void SetZColumn( const Vector3f & a_Z )
{
cell[8] = a_Z.x;
cell[9] = a_Z.y;
cell[10] = a_Z.z;
}
float cell[16];
};

18
source/MemoryLeak.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#ifdef _WIN32
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
#endif
#endif
#endif

52
source/PacketID.h Normal file
View File

@ -0,0 +1,52 @@
#pragma once
//tolua_begin
enum ENUM_PACKET_ID
{
E_KEEP_ALIVE = 0x00,
E_LOGIN = 0x01,
E_HANDSHAKE = 0x02,
E_CHAT = 0x03,
E_UPDATE_TIME = 0x04,
E_ENTITY_EQUIPMENT = 0x05,
E_USE_ENTITY = 0x07,
E_UPDATE_HEALTH = 0x08,
E_RESPAWN = 0x09,
E_FLYING = 0x0a,
E_PLAYERPOS = 0x0b,
E_PLAYERLOOK = 0x0c,
E_PLAYERMOVELOOK= 0x0d,
E_BLOCK_DIG = 0x0e,
E_BLOCK_PLACE = 0x0f,
E_ITEM_SWITCH = 0x10,
E_ADD_TO_INV = 0x11,
E_ANIMATION = 0x12,
E_PACKET_13 = 0x13,
E_NAMED_ENTITY_SPAWN = 0x14,
E_PICKUP_SPAWN = 0x15,
E_COLLECT_ITEM = 0x16,
E_ADD_VEHICLE = 0x17,
E_SPAWN_MOB = 0x18,
E_DESTROY_ENT = 0x1d,
E_ENTITY = 0x1e,
E_REL_ENT_MOVE = 0x1f,
E_ENT_LOOK = 0x20,
E_REL_ENT_MOVE_LOOK = 0x21,
E_ENT_TELEPORT = 0x22,
E_ENT_STATUS = 0x26,
E_METADATA = 0x28,
E_PRE_CHUNK = 0x32,
E_MAP_CHUNK = 0x33,
E_MULTI_BLOCK = 0x34,
E_BLOCK_CHANGE = 0x35,
E_WINDOW_OPEN = 0x64,
E_WINDOW_CLOSE = 0x65,
E_WINDOW_CLICK = 0x66,
E_INVENTORY_SLOT = 0x67,
E_INVENTORY_WHOLE = 0x68,
E_INVENTORY_PROGRESS= 0x69,
E_UPDATE_SIGN = 0x82,
E_PING = 0xfe,
E_DISCONNECT = 0xff,
};
//tolua_end

16
source/Vector3d.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "Vector3d.h"
#include "Vector3f.h"
Vector3d::Vector3d(const Vector3f & v )
: x( v.x )
, y( v.y )
, z( v.z )
{
}
Vector3d::Vector3d(const Vector3f * v )
: x( v->x )
, y( v->y )
, z( v->z )
{
}

40
source/Vector3d.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <math.h>
class Vector3f;
class Vector3d //tolua_export
{ //tolua_export
public: //tolua_export
// convert from float
Vector3d(const Vector3f & v ); //tolua_export
Vector3d(const Vector3f * v ); //tolua_export
Vector3d() : x(0), y(0), z(0) {} //tolua_export
Vector3d(double a_x, double a_y, double a_z) : x(a_x), y(a_y), z(a_z) {} //tolua_export
inline void Set(double a_x, double a_y, double a_z) { x = a_x, y = a_y, z = a_z; } //tolua_export
inline void Normalize() { double l = 1.0f / Length(); x *= l; y *= l; z *= l; } //tolua_export
inline Vector3d NormalizeCopy() { double l = 1.0f / Length(); return Vector3d( x * l, y * l, z * l ); } //tolua_export
inline void NormalizeCopy(Vector3d & a_V) { double l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); } //tolua_export
inline double Length() const { return (double)sqrt( x * x + y * y + z * z ); } //tolua_export
inline double SqrLength() const { return x * x + y * y + z * z; } //tolua_export
inline double Dot( const Vector3d & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; } //tolua_export
inline Vector3d Cross( const Vector3d & v ) const { return Vector3d( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); } //tolua_export
void operator += ( const Vector3d& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
void operator += ( Vector3d* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
void operator -= ( const Vector3d& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; }
void operator -= ( Vector3d* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
void operator *= ( double a_f ) { x *= a_f; y *= a_f; z *= a_f; }
Vector3d operator + ( const Vector3d& v2 ) const { return Vector3d( x + v2.x, y + v2.y, z + v2.z ); } //tolua_export
Vector3d operator + ( const Vector3d* v2 ) const { return Vector3d( x + v2->x, y + v2->y, z + v2->z ); } //tolua_export
Vector3d operator - ( const Vector3d& v2 ) const { return Vector3d( x - v2.x, y - v2.y, z - v2.z ); } //tolua_export
Vector3d operator - ( const Vector3d* v2 ) const { return Vector3d( x - v2->x, y - v2->y, z - v2->z ); } //tolua_export
Vector3d operator * ( const double f ) const { return Vector3d( x * f, y * f, z * f ); } //tolua_export
Vector3d operator * ( const Vector3d& v2 ) const { return Vector3d( x * v2.x, y * v2.y, z * v2.z ); } //tolua_export
double x, y, z; //tolua_export
};//tolua_export

31
source/Vector3f.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "Vector3f.h"
#include "Vector3d.h"
#include "Vector3i.h"
Vector3f::Vector3f( const Vector3d & v )
: x( (float)v.x )
, y( (float)v.y )
, z( (float)v.z )
{
}
Vector3f::Vector3f( const Vector3d * v )
: x( (float)v->x )
, y( (float)v->y )
, z( (float)v->z )
{
}
Vector3f::Vector3f( const Vector3i & v )
: x( (float)v.x )
, y( (float)v.y )
, z( (float)v.z )
{
}
Vector3f::Vector3f( const Vector3i * v )
: x( (float)v->x )
, y( (float)v->y )
, z( (float)v->z )
{
}

47
source/Vector3f.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include <math.h>
class Vector3i;
class Vector3d;
class Vector3f //tolua_export
{ //tolua_export
public: //tolua_export
Vector3f( const Vector3d & v ); //tolua_export
Vector3f( const Vector3d * v ); //tolua_export
Vector3f( const Vector3i & v ); //tolua_export
Vector3f( const Vector3i * v ); //tolua_export
Vector3f() : x(0), y(0), z(0) {} //tolua_export
Vector3f(float a_x, float a_y, float a_z) : x(a_x), y(a_y), z(a_z) {} //tolua_export
inline void Set(float a_x, float a_y, float a_z) { x = a_x, y = a_y, z = a_z; } //tolua_export
inline void Normalize() { float l = 1.0f / Length(); x *= l; y *= l; z *= l; } //tolua_export
inline Vector3f NormalizeCopy() const { float l = 1.0f / Length(); return Vector3f( x * l, y * l, z * l ); }//tolua_export
inline void NormalizeCopy(Vector3f & a_V) const { float l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); } //tolua_export
inline float Length() const { return (float)sqrtf( x * x + y * y + z * z ); } //tolua_export
inline float SqrLength() const { return x * x + y * y + z * z; } //tolua_export
inline float Dot( const Vector3f & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; } //tolua_export
inline Vector3f Cross( const Vector3f & v ) const { return Vector3f( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); } //tolua_export
inline bool Equals( const Vector3f & v ) const { return (x == v.x && y == v.y && z == v.z ); } //tolua_export
void operator += ( const Vector3f& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
void operator += ( Vector3f* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
void operator -= ( const Vector3f& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; }
void operator -= ( Vector3f* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
void operator *= ( float a_f ) { x *= a_f; y *= a_f; z *= a_f; }
void operator *= ( Vector3f* a_V ) { x *= a_V->x; y *= a_V->y; z *= a_V->z; }
void operator *= ( const Vector3f& a_V ) { x *= a_V.x; y *= a_V.y; z *= a_V.z; }
Vector3f operator + ( const Vector3f& v2 ) const { return Vector3f( x + v2.x, y + v2.y, z + v2.z ); } //tolua_export
Vector3f operator + ( const Vector3f* v2 ) const { return Vector3f( x + v2->x, y + v2->y, z + v2->z ); } //tolua_export
Vector3f operator - ( const Vector3f& v2 ) const { return Vector3f( x - v2.x, y - v2.y, z - v2.z ); } //tolua_export
Vector3f operator - ( const Vector3f* v2 ) const { return Vector3f( x - v2->x, y - v2->y, z - v2->z ); } //tolua_export
Vector3f operator * ( const float f ) const { return Vector3f( x * f, y * f, z * f ); } //tolua_export
Vector3f operator * ( const Vector3f& v2 ) const { return Vector3f( x * v2.x, y * v2.y, z * v2.z ); } //tolua_export
float x, y, z; //tolua_export
};//tolua_export

9
source/Vector3i.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "Vector3i.h"
#include "Vector3d.h"
Vector3i::Vector3i( const Vector3d & v )
: x( (int)v.x )
, y( (int)v.y )
, z( (int)v.z )
{
}

37
source/Vector3i.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <math.h>
class Vector3d;
class Vector3i //tolua_export
{ //tolua_export
public: //tolua_export
Vector3i( const Vector3d & v ); //tolua_export
Vector3i() : x(0), y(0), z(0) {} //tolua_export
Vector3i(int a_x, int a_y, int a_z) : x(a_x), y(a_y), z(a_z) {} //tolua_export
inline void Set(int a_x, int a_y, int a_z) { x = a_x, y = a_y, z = a_z; } //tolua_export
inline float Length() const { return sqrtf( (float)( x * x + y * y + z * z) ); } //tolua_export
inline int SqrLength() const { return x * x + y * y + z * z; } //tolua_export
inline bool Equals( const Vector3i & v ) const { return (x == v.x && y == v.y && z == v.z ); } //tolua_export
inline bool Equals( const Vector3i * v ) const { return (x == v->x && y == v->y && z == v->z ); } //tolua_export
void operator += ( const Vector3i& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
void operator += ( Vector3i* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
void operator -= ( const Vector3i& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; }
void operator -= ( Vector3i* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
void operator *= ( int a_f ) { x *= a_f; y *= a_f; z *= a_f; }
friend Vector3i operator + ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z ); }
friend Vector3i operator + ( const Vector3i& v1, Vector3i* v2 ) { return Vector3i( v1.x + v2->x, v1.y + v2->y, v1.z + v2->z ); }
friend Vector3i operator - ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z ); }
friend Vector3i operator - ( const Vector3i& v1, Vector3i* v2 ) { return Vector3i( v1.x - v2->x, v1.y - v2->y, v1.z - v2->z ); }
friend Vector3i operator - ( const Vector3i* v1, Vector3i& v2 ) { return Vector3i( v1->x - v2.x, v1->y - v2.y, v1->z - v2.z ); }
friend Vector3i operator * ( const Vector3i& v, const int f ) { return Vector3i( v.x * f, v.y * f, v.z * f ); }
friend Vector3i operator * ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x * v2.x, v1.y * v2.y, v1.z * v2.z ); }
friend Vector3i operator * ( const int f, const Vector3i& v ) { return Vector3i( v.x * f, v.y * f, v.z * f ); }
int x, y, z; //tolua_export
}; //tolua_export

194
source/cAuthenticator.cpp Normal file
View File

@ -0,0 +1,194 @@
#include "cAuthenticator.h"
#include "cBlockingTCPLink.h"
#include "cMCLogger.h"
#include "../iniFile/iniFile.h"
#ifndef _WIN32
#include <cstring>
#endif
#include <string>
#include <sstream>
extern void ReplaceString( std::string & a_HayStack, const std::string & a_Needle, const std::string & a_ReplaceWith );
cAuthenticator::cAuthenticator()
{
}
cAuthenticator::~cAuthenticator()
{
}
bool cAuthenticator::Authenticate( const char* a_PlayerName, const char* a_ServerID )
{
// Default values
std::string Server = "www.minecraft.net";
std::string Address = "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%";
bool bAuthenticate = true;
// Read custom values from INI
cIniFile IniFile("settings.ini");
if( IniFile.ReadFile() )
{
std::string tServer = IniFile.GetValue("Authentication", "Server");
std::string tAddress = IniFile.GetValue("Authentication", "Address");
bAuthenticate = IniFile.GetValueB("Authentication", "Authenticate", true);
bool bSave = false;
if( tServer.length() == 0 )
{
IniFile.SetValue("Authentication", "Server", Server, true );
bSave = true;
}
else
Server = tServer;
if( tAddress.length() == 0 )
{
IniFile.SetValue("Authentication", "Address", Address, true );
bSave = true;
}
else
Address = tAddress;
if( bSave )
{
IniFile.SetValueB("Authentication", "Authenticate", bAuthenticate, true );
IniFile.WriteFile();
}
}
if( !bAuthenticate ) // If we don't want to authenticate.. just return true
{
return true;
}
ReplaceString( Address, "%USERNAME%", a_PlayerName );
ReplaceString( Address, "%SERVERID%", a_ServerID );
cBlockingTCPLink TCPLink;
if( TCPLink.Connect( Server.c_str(), 80 ) )
{
//TCPLink.SendMessage( std::string( "GET /game/checkserver.jsp?user=" + std::string(a_PlayerName) + "&serverId=" + std::string(a_ServerID) + " HTTP/1.0\r\n\r\n" ).c_str() );
TCPLink.SendMessage( std::string( "GET " + Address + " HTTP/1.0\r\n\r\n" ).c_str() );
//LOGINFO("Successfully connected to mc.net");
std::string Received = TCPLink.ReceiveData();
//LOGINFO("Received data: %s", Received.c_str() );
return ParseReceived( Received.c_str(), &TCPLink );
}
else
{
LOGERROR("Could not connect to %s to verify player name! (%s)", Server.c_str(), a_PlayerName );
return false;
}
}
bool cAuthenticator::ParseReceived( const char* a_Data, cBlockingTCPLink* a_TCPLink )
{
std::stringstream ss(a_Data);
std::string temp;
ss >> temp;
//LOGINFO("tmp: %s", temp.c_str() );
bool bRedirect = false;
bool bOK = false;
if( temp.compare("HTTP/1.1") == 0 || temp.compare("HTTP/1.0") == 0 )
{
int code;
ss >> code;
if( code == 302 )
{
// redirect blabla
LOGINFO("Need to redirect!");
bRedirect = true;
}
else if( code == 200 )
{
LOGINFO("Got 200 OK :D");
bOK = true;
}
}
else
return false;
if( bRedirect )
{
std::string Location;
// Search for "Location:"
bool bFoundLocation = false;
while( !bFoundLocation && ss.good() )
{
char c = 0;
while( c != '\n' )
{
ss.get( c );
}
std::string Name;
ss >> Name;
if( Name.compare("Location:") == 0 )
{
bFoundLocation = true;
ss >> Location;
}
}
if( !bFoundLocation )
{
LOGERROR("Could not find location");
return false;
}
Location = Location.substr( strlen("http://"), std::string::npos ); // Strip http://
std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address
Location = Location.substr( Server.length(), std::string::npos );
//LOGINFO("Got location: (%s)", Location.c_str() );
//LOGINFO("Got server addr: (%s)", Server.c_str() );
a_TCPLink->CloseSocket();
if( a_TCPLink->Connect( Server.c_str(), 80 ) )
{
LOGINFO("Successfully connected to %s", Server.c_str() );
a_TCPLink->SendMessage( ( std::string("GET ") + Location + " HTTP/1.0\r\n\r\n").c_str() );
std::string Received = a_TCPLink->ReceiveData();
//LOGINFO("Received data: %s", Received.c_str() );
return ParseReceived( Received.c_str(), a_TCPLink );
}
else
{
LOGERROR("Could not connect to %s to verify player name!", Server.c_str() );
}
}
else if( bOK )
{
// Header says OK, so receive the rest.
// Go past header, double \n means end of headers
char c = 0;
while( ss.good() )
{
while( c != '\n' )
{
ss.get( c );
}
ss.get( c );
if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' )
break;
}
if( !ss.good() ) return false;
std::string Result;
ss >> Result;
LOGINFO("Got result: %s", Result.c_str() );
if( Result.compare("YES") == 0 )
{
LOGINFO("Result was \"YES\", so player is authenticated!");
return true;
}
else
{
LOGINFO("Result was \"%s\", so player is NOT authenticated!", Result.c_str() );
return false;
}
}
return false;
}

13
source/cAuthenticator.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
class cBlockingTCPLink;
class cAuthenticator
{
public:
cAuthenticator();
~cAuthenticator();
bool Authenticate( const char* a_PlayerName, const char* a_ServerID );
private:
bool ParseReceived( const char* a_Data, cBlockingTCPLink* a_TCPLink );
};

34
source/cBlockEntity.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#ifndef _WIN32
#include "BlockID.h"
#else
enum ENUM_BLOCK_ID;
#endif
class cClientHandle;
class cPlayer;
class cBlockEntity
{
protected:
cBlockEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z) : m_PosX( a_X ), m_PosY( a_Y ), m_PosZ( a_Z ), m_BlockType( a_BlockType ) {}
public:
virtual ~cBlockEntity() {};
virtual void Destroy() {};
int GetPosX() { return m_PosX; }
int GetPosY() { return m_PosY; }
int GetPosZ() { return m_PosZ; }
ENUM_BLOCK_ID GetBlockType() { return m_BlockType; }
virtual void UsedBy( cPlayer & a_Player ) = 0;
virtual void SendTo( cClientHandle* a_Client ) { (void)a_Client; }
protected:
int m_PosX; // Position in block coordinates
int m_PosY;
int m_PosZ;
ENUM_BLOCK_ID m_BlockType;
};

37
source/cBlockToPickup.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "cBlockToPickup.h"
#include "BlockID.h"
ENUM_ITEM_ID cBlockToPickup::ToPickup( unsigned char a_BlockID, ENUM_ITEM_ID a_UsedItemID )
{
(void)a_UsedItemID;
switch( a_BlockID )
{
case E_BLOCK_AIR:
return E_ITEM_EMPTY;
case E_BLOCK_STONE:
return E_ITEM_COBBLESTONE;
case E_BLOCK_GRASS:
return E_ITEM_DIRT;
case E_BLOCK_DIRT:
return E_ITEM_DIRT;
case E_BLOCK_LOG:
return E_ITEM_LOG;
case E_BLOCK_COAL_ORE:
return E_ITEM_COAL;
case E_BLOCK_DIAMOND_ORE:
return E_ITEM_DIAMOND;
case E_BLOCK_IRON_BLOCK:
return E_ITEM_IRON_BLOCK;
case E_BLOCK_DIAMOND_BLOCK:
return E_ITEM_DIAMOND_BLOCK;
case E_BLOCK_GOLD_BLOCK:
return E_ITEM_GOLD_BLOCK;
case E_BLOCK_SIGN_POST:
case E_BLOCK_WALLSIGN:
return E_ITEM_SIGN;
default:
return (ENUM_ITEM_ID)a_BlockID;
}
return E_ITEM_EMPTY;
}

13
source/cBlockToPickup.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#ifndef _WIN32
#include "BlockID.h"
#else
enum ENUM_ITEM_ID;
#endif
class cBlockToPickup
{
public:
static ENUM_ITEM_ID ToPickup( unsigned char a_BlockID, ENUM_ITEM_ID a_UsedItemID );
};

137
source/cBlockingTCPLink.cpp Normal file
View File

@ -0,0 +1,137 @@
#include "cBlockingTCPLink.h"
#include "packets/cPacket.h"
#include <string>
#include "cMCLogger.h"
#ifndef _WIN32
#include <cstring>
#include <errno.h>
#endif
#ifdef _WIN32
#define MSG_NOSIGNAL (0)
#endif
#ifdef __MACH__
#define MSG_NOSIGNAL (0)
#endif
cBlockingTCPLink::cBlockingTCPLink()
: m_Socket( 0 )
{
}
cBlockingTCPLink::~cBlockingTCPLink()
{
CloseSocket();
}
void cBlockingTCPLink::CloseSocket()
{
if( m_Socket )
{
closesocket( m_Socket );
m_Socket = 0;
}
}
bool cBlockingTCPLink::Connect( const char* a_Address, unsigned int a_Port )
{
if( m_Socket )
{
LOGWARN("WARNING: cTCPLink Connect() called while still connected. ALWAYS disconnect before re-connecting!");
}
struct hostent *hp;
unsigned int addr;
struct sockaddr_in server;
#ifdef _WIN32
WSADATA wsaData;
int wsaret=WSAStartup(/*0x101*/ MAKEWORD(2, 2),&wsaData);
if(wsaret!=0)
{
LOGERROR("cTCPLink: WSAStartup returned error");
return false;
}
#endif
m_Socket=socket(AF_INET,SOCK_STREAM,0);
#ifdef _WIN32
if( m_Socket==INVALID_SOCKET )
#else
if( m_Socket < 0 )
#endif
{
LOGERROR("cTCPLink: Invalid socket");
m_Socket = 0;
return false;
}
addr=inet_addr( a_Address );
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
if(hp==NULL)
{
//LOGWARN("cTCPLink: gethostbyaddr returned NULL");
hp = gethostbyname( a_Address );
if( hp == NULL )
{
LOGWARN("cTCPLink: Could not resolve %s", a_Address);
CloseSocket();
return false;
}
}
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons( (unsigned short)a_Port );
if( connect( m_Socket, (struct sockaddr*)&server, sizeof(server) ) )
{
LOGWARN("cTCPLink: No response from server (%i)", errno);
CloseSocket();
return false;
}
return true;
}
int cBlockingTCPLink::Send( char* a_Data, unsigned int a_Size, int a_Flags /* = 0 */ )
{
if( !m_Socket )
{
LOGWARN("cBlockingTCPLink: Trying to send data without a valid connection!");
return -1;
}
return cPacket::SendData( m_Socket, a_Data, a_Size, a_Flags );
}
int cBlockingTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ )
{
if( !m_Socket )
{
LOGWARN("cBlockingTCPLink: Trying to send message without a valid connection!");
return -1;
}
return cPacket::SendData( m_Socket, a_Message, strlen(a_Message), a_Flags );
}
std::string cBlockingTCPLink::ReceiveData()
{
if( !m_Socket ) return "";
int Received = 0;
char Buffer[256];
std::string Data;
while( (Received = recv(m_Socket, Buffer, 256, 0) ) > 0 )
{
//LOGINFO("Recv: %i", Received);
//LOG("%s", Buffer );
Data.append( Buffer, Received );
memset( Buffer, 0, 256 );
}
//LOGINFO("Received returned: %i", Received );
return Data;
}

21
source/cBlockingTCPLink.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "cSocket.h"
#include <string>
class cEvent;
class cBlockingTCPLink //tolua_export
{ //tolua_export
public: //tolua_export
cBlockingTCPLink(); //tolua_export
~cBlockingTCPLink(); //tolua_export
bool Connect( const char* a_Address, unsigned int a_Port ); //tolua_export
int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); //tolua_export
int SendMessage( const char* a_Message, int a_Flags = 0 ); //tolua_export
void CloseSocket(); //tolua_export
std::string ReceiveData(); //tolua_export
protected:
cSocket m_Socket;
}; //tolua_export

25
source/cChatColor.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "cChatColor.h"
const std::string cChatColor::Color = "§";
const std::string cChatColor::Delimiter = "\xa7";
const std::string cChatColor::Black = cChatColor::Color + "0";
const std::string cChatColor::Navy = cChatColor::Color + "1";
const std::string cChatColor::Green = cChatColor::Color + "2";
const std::string cChatColor::Blue = cChatColor::Color + "3";
const std::string cChatColor::Red = cChatColor::Color + "4";
const std::string cChatColor::Purple = cChatColor::Color + "5";
const std::string cChatColor::Gold = cChatColor::Color + "6";
const std::string cChatColor::LightGray = cChatColor::Color + "7";
const std::string cChatColor::Gray = cChatColor::Color + "8";
const std::string cChatColor::DarkPurple = cChatColor::Color + "9";
const std::string cChatColor::LightGreen = cChatColor::Color + "a";
const std::string cChatColor::LightBlue = cChatColor::Color + "b";
const std::string cChatColor::Rose = cChatColor::Color + "c";
const std::string cChatColor::LightPurple = cChatColor::Color + "d";
const std::string cChatColor::Yellow = cChatColor::Color + "e";
const std::string cChatColor::White = cChatColor::Color + "f";
const std::string cChatColor::MakeColor( char a_Color )
{
return cChatColor::Color + a_Color;
}

32
source/cChatColor.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <string>
// tolua_begin
class cChatColor
{
public:
static const std::string Color;
static const std::string Delimiter;
static const std::string Black;
static const std::string Navy;
static const std::string Green;
static const std::string Blue;
static const std::string Red;
static const std::string Purple;
static const std::string Gold;
static const std::string LightGray;
static const std::string Gray;
static const std::string DarkPurple;
static const std::string LightGreen;
static const std::string LightBlue;
static const std::string Rose;
static const std::string LightPurple;
static const std::string Yellow;
static const std::string White;
static const std::string MakeColor( char a_Color );
};
// tolua_end

185
source/cChestEntity.cpp Normal file
View File

@ -0,0 +1,185 @@
#include "cChestEntity.h"
#include "cItem.h"
#include <string>
#include "cClientHandle.h"
#include "cMCLogger.h"
#include "cPlayer.h"
#include "cWindow.h"
#include "cPickup.h"
#include "cMCLogger.h"
#include <json/json.h>
cChestEntity::cChestEntity(int a_X, int a_Y, int a_Z)
: cBlockEntity( E_BLOCK_CHEST, a_X, a_Y, a_Z )
{
m_Content = new cItem[ c_ChestHeight*c_ChestWidth ];
}
cChestEntity::~cChestEntity()
{
if( GetWindow() )
{
GetWindow()->OwnerDestroyed();
}
if( m_Content )
{
delete [] m_Content;
}
}
void cChestEntity::Destroy()
{
// Drop items
for( int i = 0; i < c_ChestHeight*c_ChestWidth; ++i )
{
if( !m_Content[i].IsEmpty() )
{
cPickup* Pickup = new cPickup( m_PosX*32 + 16, m_PosY*32 + 16, m_PosZ*32 + 16, m_Content[i], 0, 1.f, 0 );
Pickup->Initialize();
m_Content[i].Empty();
}
}
}
cItem * cChestEntity::GetSlot( int a_Slot )
{
if( a_Slot > -1 && a_Slot < c_ChestHeight*c_ChestWidth )
{
return &m_Content[ a_Slot ];
}
return 0;
}
void cChestEntity::SetSlot( int a_Slot, cItem & a_Item )
{
if( a_Slot > -1 && a_Slot < c_ChestHeight*c_ChestWidth )
{
m_Content[a_Slot] = a_Item;
}
}
void cChestEntity::WriteToFile(FILE* a_File)
{
fwrite( &m_BlockType, sizeof( ENUM_BLOCK_ID ), 1, a_File );
fwrite( &m_PosX, sizeof( int ), 1, a_File );
fwrite( &m_PosY, sizeof( int ), 1, a_File );
fwrite( &m_PosZ, sizeof( int ), 1, a_File );
unsigned int NumSlots = c_ChestHeight*c_ChestWidth;
fwrite( &NumSlots, sizeof(unsigned int), 1, a_File );
for(unsigned int i = 0; i < NumSlots; i++)
{
cItem* Item = GetSlot( i );
if( Item )
{
fwrite( &Item->m_ItemID, sizeof(Item->m_ItemID), 1, a_File );
fwrite( &Item->m_ItemCount, sizeof(Item->m_ItemCount), 1, a_File );
fwrite( &Item->m_ItemHealth, sizeof(Item->m_ItemHealth), 1, a_File );
}
}
}
bool cChestEntity::LoadFromFile(FILE* a_File)
{
if( fread( &m_PosX, sizeof(int), 1, a_File) != 1 ) { LOGERROR("ERROR READING CHEST FROM FILE"); return false; }
if( fread( &m_PosY, sizeof(int), 1, a_File) != 1 ) { LOGERROR("ERROR READING CHEST FROM FILE"); return false; }
if( fread( &m_PosZ, sizeof(int), 1, a_File) != 1 ) { LOGERROR("ERROR READING CHEST FROM FILE"); return false; }
unsigned int NumSlots = 0;
if( fread( &NumSlots, sizeof(unsigned int), 1, a_File) != 1 ) { LOGERROR("ERROR READING CHEST FROM FILE"); return false; }
for(unsigned int i = 0; i < NumSlots; i++)
{
cItem Item;
if( fread( &Item.m_ItemID, sizeof(Item.m_ItemID), 1, a_File) != 1 ) { LOGERROR("ERROR READING CHEST FROM FILE"); return false; }
if( fread( &Item.m_ItemCount, sizeof(Item.m_ItemCount), 1, a_File) != 1 ) { LOGERROR("ERROR READING CHEST FROM FILE"); return false; }
if( fread( &Item.m_ItemHealth, sizeof(Item.m_ItemHealth), 1, a_File)!= 1 ) { LOGERROR("ERROR READING CHEST FROM FILE"); return false; }
SetSlot( i, Item );
}
return true;
}
bool cChestEntity::LoadFromJson( const Json::Value& a_Value )
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
Json::Value AllSlots = a_Value.get("Slots", 0);
int SlotIdx = 0;
for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr )
{
Json::Value & Slot = *itr;
cItem Item;
Item.m_ItemID = (ENUM_ITEM_ID)Slot.get("ID", -1 ).asInt();
if( Item.m_ItemID > 0 )
{
Item.m_ItemCount = (char)Slot.get("Count", -1 ).asInt();
Item.m_ItemHealth = (short)Slot.get("Health", -1 ).asInt();
}
SetSlot( SlotIdx, Item );
SlotIdx++;
}
return true;
}
void cChestEntity::SaveToJson( Json::Value& a_Value )
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
unsigned int NumSlots = c_ChestHeight*c_ChestWidth;
Json::Value AllSlots;
for(unsigned int i = 0; i < NumSlots; i++)
{
Json::Value Slot;
cItem* Item = GetSlot( i );
if( Item )
{
Slot["ID"] = Item->m_ItemID;
if( Item->m_ItemID > 0 )
{
Slot["Count"] = Item->m_ItemCount;
Slot["Health"] = Item->m_ItemHealth;
}
}
AllSlots.append( Slot );
}
a_Value["Slots"] = AllSlots;
}
void cChestEntity::SendTo( cClientHandle* a_Client, cServer* a_Server )
{
(void)a_Client;
(void)a_Server;
return;
}
void cChestEntity::UsedBy( cPlayer & a_Player )
{
LOG("Used a chest");
// m_Content[0].m_ItemCount = 1;
// m_Content[0].m_ItemID = E_ITEM_STONE;
// m_Content[0].m_ItemHealth = 0;
if( !GetWindow() )
{
cWindow* Window = new cWindow( this, true );
Window->SetSlots( m_Content, c_ChestHeight*c_ChestWidth );
Window->SetWindowID( 1 );
Window->SetWindowType( 0 );
Window->SetWindowTitle("UberChest");
OpenWindow( Window );
}
if( GetWindow() )
{
if( a_Player.GetWindow() != GetWindow() )
{
a_Player.OpenWindow( GetWindow() );
GetWindow()->SendWholeWindow( a_Player.GetClientHandle() );
}
}
}

42
source/cChestEntity.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include "cBlockEntity.h"
#include "cWindowOwner.h"
#include "FileDefine.h"
namespace Json
{
class Value;
};
class cClientHandle;
class cServer;
class cItem;
class cNBTData;
class cChestEntity : public cBlockEntity, public cWindowOwner
{
public:
cChestEntity(int a_X, int a_Y, int a_Z);
virtual ~cChestEntity();
virtual void Destroy();
void HandleData( cNBTData* a_NBTData );
cItem * GetSlot( int a_Slot );
void SetSlot( int a_Slot, cItem & a_Item );
void WriteToFile(FILE* a_File);
bool LoadFromFile(FILE* a_File);
bool LoadFromJson( const Json::Value& a_Value );
void SaveToJson( Json::Value& a_Value );
void SendTo( cClientHandle* a_Client, cServer* a_Server );
virtual void UsedBy( cPlayer & a_Player );
static const int c_ChestWidth = 9;
static const int c_ChestHeight = 3;
private:
cItem* m_Content;
};

90
source/cChicken.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "cChicken.h"
#include "Vector3f.h"
#include "Vector3d.h"
#include "Defines.h"
#include "cRoot.h"
#include "cWorld.h"
#include "cPickup.h"
#include "cItem.h"
#include "cMCLogger.h"
#ifndef _WIN32
#include <stdlib.h> // rand()
#include <cstring>
#endif
#include <string>
cChicken::cChicken()
: m_ChaseTime( 999999 )
{
//LOG("SPAWNING A CHICKEN!!!!!!!!!!!!!!!!!!!!!");
m_EMPersonality = PASSIVE;
m_MobType = 93;
GetMonsterConfig("Chicken");
}
cChicken::~cChicken()
{
}
bool cChicken::IsA( const char* a_EntityType )
{
//LOG("IsA( cChicken ) : %s", a_EntityType);
if( strcmp( a_EntityType, "cChicken" ) == 0 ) return true;
return cMonster::IsA( a_EntityType );
}
void cChicken::Tick(float a_Dt)
{
cMonster::Tick(a_Dt);
}
void cChicken::KilledBy( cEntity* a_Killer )
{
if( (rand() % 5) == 0 )
{
cPickup* Pickup = new cPickup( (int)(m_Pos->x*32), (int)(m_Pos->y*32), (int)(m_Pos->z*32), cItem( E_ITEM_EGG, 1 ) );
Pickup->Initialize();
}
if( (rand() % 1) == 0 )
{
cPickup* Pickup = new cPickup( (int)(m_Pos->x*32), (int)(m_Pos->y*32), (int)(m_Pos->z*32), cItem( E_ITEM_FEATHER, 1 ) );
Pickup->Initialize();
}
cMonster::KilledBy( a_Killer );
}
//What to do if in Idle State
void cChicken::InStateIdle(float a_Dt) {
cMonster::InStateIdle(a_Dt);
}
//What to do if in Chasing State
void cChicken::InStateChasing(float a_Dt) {
cMonster::InStateChasing(a_Dt);
m_ChaseTime += a_Dt;
if( m_Target )
{
Vector3f Pos = Vector3f( m_Pos );
Vector3f Their = Vector3f( m_Target->GetPosition() );
if( (Their - Pos).Length() <= m_AttackRange) {
cMonster::Attack(a_Dt);
}
MoveToPosition( Their + Vector3f(0, 0.65f, 0) );
} else if( m_ChaseTime > 5.f ) {
m_ChaseTime = 0;
m_EMState = IDLE;
}
}
void cChicken::InStateEscaping(float a_Dt) {
cMonster::InStateEscaping(a_Dt);
}

21
source/cChicken.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "cMonster.h"
class cChicken : public cMonster
{
public:
cChicken();
~cChicken();
virtual bool IsA( const char* a_EntityType );
virtual void Tick(float a_Dt);
virtual void KilledBy( cEntity* a_Killer );
virtual void InStateIdle(float a_Dt);
virtual void InStateChasing(float a_Dt);
virtual void InStateEscaping(float a_Dt);
//float m_ChaseTime;
protected:
float m_ChaseTime;
};

1388
source/cChunk.cpp Normal file

File diff suppressed because it is too large Load Diff

127
source/cChunk.h Normal file
View File

@ -0,0 +1,127 @@
#pragma once
#include <list>
namespace Json
{
class Value;
};
class cFurnaceEntity;
class cPacket;
class cBlockEntity;
class cEntity;
class cClientHandle;
class cServer;
class cChunk
{
public:
cChunk(int a_X, int a_Y, int a_Z);
~cChunk();
void Initialize();
void Tick(float a_Dt);
inline int GetPosX() { return m_PosX; }
inline int GetPosY() { return m_PosY; }
inline int GetPosZ() { return m_PosZ; }
void Send( cClientHandle* a_Client );
void AsyncUnload( cClientHandle* a_Client );
void SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta );
void FastSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta );
char GetBlock( int a_X, int a_Y, int a_Z );
char GetBlock( int a_BlockIdx );
cBlockEntity* GetBlockEntity( int a_X, int a_Y, int a_Z );
void RemoveBlockEntity( cBlockEntity* a_BlockEntity );
void AddBlockEntity( cBlockEntity* a_BlockEntity );
char GetHeight( int a_X, int a_Z );
void SendBlockTo( int a_X, int a_Y, int a_Z, cClientHandle* a_Client );
void AddClient( cClientHandle* a_Client );
void RemoveClient( cClientHandle* a_Client );
std::list< cEntity* > & GetEntities();// { return m_Entities; }
void AddEntity( cEntity & a_Entity );
bool RemoveEntity( cEntity & a_Entity, cChunk* a_CalledFrom = 0 );
void LockEntities();
void UnlockEntities();
const std::list< cClientHandle* > & GetClients();// { return m_LoadedByClient; }
inline void RecalculateLighting() { m_bCalculateLighting = true; } // Recalculate lighting next tick
inline void RecalculateHeightmap() { m_bCalculateHeightmap = true; } // Recalculate heightmap next tick
void SpreadLight(char* a_LightBuffer);
bool SaveToDisk();
bool LoadFromDisk();
// Broadcasts to all clients that have loaded this chunk
void Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude = 0 ) const;
char* pGetBlockData() { return m_BlockData; }
char* pGetType() { return m_BlockType; }
char* pGetMeta() { return m_BlockMeta; }
char* pGetLight() { return m_BlockLight; }
char* pGetSkyLight() { return m_BlockSkyLight; }
char GetLight(char* a_Buffer, int a_BlockIdx);
char GetLight(char* a_Buffer, int x, int y, int z);
void SetLight(char* a_Buffer, int a_BlockIdx, char a_Light);
void SetLight(char* a_Buffer, int x, int y, int z, char light);
void AddTickBlockEntity( cFurnaceEntity* a_Entity );
//{
// m_TickBlockEntities.remove( a_Entity );
// m_TickBlockEntities.push_back( a_Entity );
//}
void RemoveTickBlockEntity( cFurnaceEntity* a_Entity );
//{
// m_TickBlockEntities.remove( a_Entity );
//}
static const int c_NumBlocks = 16*128*16;
static const int c_BlockDataSize = c_NumBlocks * 2 + (c_NumBlocks/2); // 2.5 * numblocks
private:
struct sChunkState;
sChunkState* m_pState;
friend class cChunkMap; // So it has access to buffers and shit
void LoadFromJson( const Json::Value & a_Value );
void SaveToJson( Json::Value & a_Value );
void GenerateTerrain();
void CalculateLighting(); // Recalculate right now
void CalculateHeightmap();
void SpreadLightOfBlock(char* a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff);
void SpreadLightOfBlockX(char* a_LightBuffer, int a_X, int a_Y, int a_Z);
void SpreadLightOfBlockY(char* a_LightBuffer, int a_X, int a_Y, int a_Z);
void SpreadLightOfBlockZ(char* a_LightBuffer, int a_X, int a_Y, int a_Z);
void CreateBlockEntities();
unsigned int MakeIndex(int x, int y, int z );
bool m_bCalculateLighting;
bool m_bCalculateHeightmap;
int m_PosX, m_PosY, m_PosZ;
char m_BlockData[c_BlockDataSize]; // Chunk data ready to be compressed and sent
char *m_BlockType; // Pointers to an element in m_BlockData
char *m_BlockMeta; // += NumBlocks
char *m_BlockLight; // += NumBlocks/2
char *m_BlockSkyLight; // += NumBlocks/2
unsigned char m_HeightMap[16*16];
unsigned int m_BlockTickNum;
unsigned int m_BlockTickX, m_BlockTickY, m_BlockTickZ;
void* m_EntitiesCriticalSection;
};

364
source/cChunkLoader.cpp Normal file
View File

@ -0,0 +1,364 @@
#if 0 // ignore all contents of this file
#include "cChunkLoader.h"
#include "cChunk.h"
#include "cMCLogger.h"
#include "BlockID.h"
#include "cCriticalSection.h"
#include "cEvent.h"
#include "cThread.h"
#include "cSleep.h"
#include "cChestEntity.h"
#include "cFurnaceEntity.h"
#include "cSignEntity.h"
#include <iostream>
#ifndef _WIN32
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <sys/stat.h> // for mkdir
#include <sys/types.h>
#else
#include <Windows.h>
#endif
#include <list>
struct ChunkData
{
ChunkData()
: x( 0 )
, z( 0 )
, Data( 0 )
, LiveChunk( 0 )
{}
int x, z;
unsigned int DataSize;
unsigned int ChunkStart;
char* Data;
cChunk* LiveChunk;
};
typedef std::list< ChunkData > ChunkDataList;
struct cChunkLoader::ChunkPack
{
ChunkDataList AllChunks;
int x, y, z;
};
typedef std::list< cChunkLoader::ChunkPack > ChunkPackList;
struct cChunkLoader::ChunkPacks
{
ChunkPackList AllPacks;
};
cChunkLoader::cChunkLoader()
: m_bStop( false )
, m_CriticalSection( new cCriticalSection() )
, m_Event( new cEvent() )
, m_ChunkPacks( new ChunkPacks )
{
cThread( SaveThread, this );
}
cChunkLoader::~cChunkLoader()
{
m_bStop = true;
m_Event->Wait();
delete m_CriticalSection;
}
void cChunkLoader::SaveThread( void* a_Param )
{
cChunkLoader* self = (cChunkLoader*)a_Param;
while( !self->m_bStop )
{
cSleep::MilliSleep( 1000 ); // Only check for saving once a second
}
self->m_Event->Set();
}
cChunk* cChunkLoader::LoadChunk( int a_X, int a_Y, int a_Z )
{
m_CriticalSection->Lock();
cChunk* Chunk = 0;
Chunk = LoadOldFormat( a_X, a_Y, a_Z );
if( Chunk ) { Chunk->CalculateHeightmap(); }
else
{
1; // load new format()
}
m_CriticalSection->Unlock();
return Chunk;
}
bool cChunkLoader::SaveChunk( const cChunk & a_Chunk )
{
m_CriticalSection->Lock();
bool Success = SaveOldFormat( a_Chunk );
m_CriticalSection->Unlock();
return Success;
}
/**************************************************
* Old format stuffs
**/
cChunk* cChunkLoader::LoadOldFormat( int a_X, int a_Y, int a_Z )
{
char SourceFile[128];
sprintf_s(SourceFile, 128, "world/X%i_Y%i_Z%i.bin", a_X, a_Y, a_Z );
FILE* f = 0;
#ifdef _WIN32
if( fopen_s(&f, SourceFile, "rb" ) == 0 ) // no error
#else
if( (f = fopen(SourceFile, "rb" )) != 0 ) // no error
#endif
{
cChunk* Chunk = new cChunk( a_X, a_Y, a_Z );
if( fread( Chunk->m_BlockData, sizeof(char)*cChunk::c_BlockDataSize, 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
// Now load Block Entities
ENUM_BLOCK_ID BlockType;
while( fread( &BlockType, sizeof(ENUM_BLOCK_ID), 1, f) == 1 )
{
switch( BlockType )
{
case E_BLOCK_CHEST:
{
cChestEntity* ChestEntity = new cChestEntity( 0, 0, 0 );
if( !ChestEntity->LoadFromFile( f ) )
{
LOGERROR("ERROR READING CHEST FROM FILE %s", SourceFile );
delete ChestEntity;
fclose(f);
return false;
}
Chunk->m_BlockEntities.push_back( ChestEntity );
}
break;
case E_BLOCK_FURNACE:
{
cFurnaceEntity* FurnaceEntity = new cFurnaceEntity( 0, 0, 0 );
if( !FurnaceEntity->LoadFromFile( f ) )
{
LOGERROR("ERROR READING FURNACE FROM FILE %s", SourceFile );
delete FurnaceEntity;
fclose(f);
return false;
}
Chunk->m_BlockEntities.push_back( FurnaceEntity );
Chunk->m_TickBlockEntities.push_back( FurnaceEntity ); // They need tickin'
}
break;
case E_BLOCK_SIGN_POST:
case E_BLOCK_WALLSIGN:
{
cSignEntity* SignEntity = new cSignEntity(BlockType, 0, 0, 0 );
if( !SignEntity->LoadFromFile( f ) )
{
LOGERROR("ERROR READING SIGN FROM FILE %s", SourceFile );
delete SignEntity;
fclose(f);
return false;
}
Chunk->m_BlockEntities.push_back( SignEntity );
}
break;
default:
break;
}
}
fclose(f);
return Chunk;
}
else
{
return 0;
}
}
bool cChunkLoader::SaveOldFormat( const cChunk & a_Chunk )
{
char SourceFile[128];
sprintf_s(SourceFile, 128, "world/X%i_Y%i_Z%i.bin", a_Chunk.m_PosX, a_Chunk.m_PosY, a_Chunk.m_PosZ );
#ifdef _WIN32
{
SECURITY_ATTRIBUTES Attrib;
Attrib.nLength = sizeof(SECURITY_ATTRIBUTES);
Attrib.lpSecurityDescriptor = NULL;
Attrib.bInheritHandle = false;
::CreateDirectory("world", &Attrib);
}
#else
{
mkdir("world", S_IRWXU | S_IRWXG | S_IRWXO);
}
#endif
FILE* f = 0;
#ifdef _WIN32
if( fopen_s(&f, SourceFile, "wb" ) == 0 ) // no error
#else
if( (f = fopen(SourceFile, "wb" )) != 0 ) // no error
#endif
{
fwrite( a_Chunk.m_BlockData, sizeof(char)*cChunk::c_BlockDataSize, 1, f );
// Now write Block Entities
for( std::list<cBlockEntity*>::const_iterator itr = a_Chunk.m_BlockEntities.begin(); itr != a_Chunk.m_BlockEntities.end(); ++itr)
{
cBlockEntity* BlockEntity = *itr;
switch( BlockEntity->GetBlockType() )
{
case E_BLOCK_CHEST:
{
cChestEntity* ChestEntity = reinterpret_cast< cChestEntity* >( BlockEntity );
ChestEntity->WriteToFile( f );
}
break;
case E_BLOCK_FURNACE:
{
cFurnaceEntity* FurnaceEntity = reinterpret_cast< cFurnaceEntity* >( BlockEntity );
FurnaceEntity->WriteToFile( f );
}
break;
case E_BLOCK_SIGN_POST:
case E_BLOCK_WALLSIGN:
{
cSignEntity* SignEntity = reinterpret_cast< cSignEntity* >( BlockEntity );
SignEntity->WriteToFile( f );
}
break;
default:
break;
}
}
fclose(f);
return true;
}
else
{
LOGERROR("ERROR WRITING TO FILE %s", SourceFile);
return false;
}
}
/******************************************
* New format
**/
cChunk* cChunkLoader::LoadFormat1( int a_X, int a_Y, int a_Z )
{
int PakX = (int)(floorf((float)a_X / 16.f));
int PakY = (int)(floorf((float)a_Y / 16.f));
int PakZ = (int)(floorf((float)a_Z / 16.f));
ChunkPack * Pack = 0;
ChunkPackList & PackList = m_ChunkPacks->AllPacks;
for( ChunkPackList::iterator itr = PackList.begin(); itr != PackList.end(); ++itr )
{
if( itr->x == PakX && itr->y == PakY && itr->z == PakZ )
{
Pack = &(*itr);
break;
}
}
if( !Pack ) // The pack was not in memory, so try to load it from disk
{
Pack = LoadPak1( PakX, PakY, PakZ ); // Load .pak file from disk
if( Pack )
{
PackList.push_back( *Pack ); // Add it to the loaded list
}
}
if( Pack ) // Allright, the pack is in memory
{
ChunkData * Data = 0;
ChunkDataList & ChunkList = Pack->AllChunks;
for( ChunkDataList::iterator itr = ChunkList.begin(); itr != ChunkList.end(); ++itr )
{
if( itr->x == a_X && itr->z == a_Z )
{
Data = &(*itr);
break;
}
}
if( !Data ) // Sorry, chunk does not exist (yet)
return 0;
if( Data->LiveChunk ) // This chunk is already loaded and decoded (this should actually never happen)
return Data->LiveChunk;
// Decompress chunk, and return brand new chunk
// doing it...
return 0; // actually return the chunk
}
return 0; // .pak file didn't exist
}
cChunkLoader::ChunkPack* cChunkLoader::LoadPak1( int PakX, int PakY, int PakZ )
{
char SourceFile[128];
sprintf_s(SourceFile, 128, "world/X%i_Y%i_Z%i.pak", PakX, PakY, PakZ );
FILE* f = 0;
#ifdef _WIN32
if( fopen_s(&f, SourceFile, "rb" ) == 0 ) // no error
#else
if( (f = fopen(SourceFile, "rb" )) != 0 ) // no error
#endif
{
cChunkLoader::ChunkPack * Pack = new cChunkLoader::ChunkPack;
Pack->x = PakX;
Pack->y = PakY;
Pack->z = PakZ;
short Version = 0;
if( fread( &Version, sizeof( short ), 1, f ) != 1 ) { LOGERROR("Error reading file %s", SourceFile ); return 0; }
if( Version != 1 ) { LOGERROR("Wrong pak version! %s", SourceFile ); return 0; }
short NumChunks = 0;
if( fread( &NumChunks, sizeof( short ), 1, f ) != 1 ) { LOGERROR("Error reading file %s", SourceFile ); return 0; }
// Load all the headers
for( short i = 0; i < NumChunks; ++i )
{
ChunkData Data;
if( fread( &Data.x, sizeof( int ), 1, f ) != 1 ) { LOGERROR("Error reading file %s", SourceFile ); return 0; }
if( fread( &Data.z, sizeof( int ), 1, f ) != 1 ) { LOGERROR("Error reading file %s", SourceFile ); return 0; }
if( fread( &Data.DataSize, sizeof( unsigned int ), 1, f ) != 1 ) { LOGERROR("Error reading file %s", SourceFile ); return 0; }
if( fread( &Data.ChunkStart, sizeof( unsigned int ), 1, f ) != 1 ) { LOGERROR("Error reading file %s", SourceFile ); return 0; }
Pack->AllChunks.push_back( Data );
}
// Load all compressed chunk data in the order the headers were loaded
ChunkDataList::iterator itr = Pack->AllChunks.begin();
for( short i = 0; i < NumChunks; ++i )
{
itr->Data = new char[ itr->DataSize ];
if( fread( itr->Data, sizeof( char ) * itr->DataSize, 1, f ) != 1 ) { LOGERROR("Error reading file %s", SourceFile ); return 0; }
++itr;
}
// And we're done :)
return Pack;
}
return 0;
}
#endif

32
source/cChunkLoader.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
class cCriticalSection;
class cEvent;
class cChunk;
class cChunkLoader
{
public:
cChunkLoader();
~cChunkLoader();
cChunk* LoadChunk( int a_X, int a_Y, int a_Z );
bool SaveChunk( const cChunk & a_Chunk );
struct ChunkPack;
private:
cChunk* LoadFormat1( int a_X, int a_Y, int a_Z );
ChunkPack* LoadPak1( int PakX, int PakY, int PakZ ); // This loads a .pak file from disk and returns it, nothing more
// Old stuffs
cChunk* LoadOldFormat( int a_X, int a_Y, int a_Z );
bool SaveOldFormat( const cChunk & a_Chunk );
static void SaveThread( void* a_Param );
bool m_bStop;
cCriticalSection* m_CriticalSection;
cEvent* m_Event;
struct ChunkPacks; // Defined in .cpp
ChunkPacks* m_ChunkPacks;
};

714
source/cChunkMap.cpp Normal file
View File

@ -0,0 +1,714 @@
#include "cChunkMap.h"
#include "cChunk.h"
#include "cMCLogger.h"
#include "cWorld.h"
#include "cRoot.h"
#include "cMakeDir.h"
#ifndef _WIN32
#include <cstring> // memcpy
#include <cstdlib> // abs
#endif
#include "zlib.h"
#include <json/json.h>
#define USE_MEMCPY
#define LAYER_SIZE (32)
cChunkMap::cChunkMap( int a_Width, int a_Height )
: m_Nodes( new cChunkNode[ a_Width * a_Height ] )
, m_Width( a_Width )
, m_Height( a_Height )
, m_Layers( 0 )
, m_NumLayers( 0 )
{
}
cChunkMap::~cChunkMap()
{
delete [] m_Nodes;
}
cChunkMap::cChunkNode::cChunkNode()
{
m_Size = 0;
m_Allocated = 0;
m_Chunks = 0;
}
cChunkMap::cChunkNode::~cChunkNode()
{
if( m_Allocated > 0 )
{
for( unsigned int i = 0; i < m_Size; ++i )
{
delete m_Chunks[i];
}
delete [] m_Chunks;
}
// m_Chunks = 0;
// m_Allocated = 0;
// m_Size = 0;
}
void cChunkMap::cChunkNode::push_back( cChunk* a_Chunk )
{
if( m_Allocated == 0 )
{
resize( 1 );
}
if( m_Size >= m_Allocated )
{
resize( m_Allocated*2 );
}
m_Chunks[ m_Size ] = a_Chunk;
m_Size++;
}
void cChunkMap::cChunkNode::resize( unsigned int a_NewSize )
{
cChunk** TempChunks = new cChunk*[a_NewSize];
if( m_Allocated > 0 )
{
#ifdef USE_MEMCPY
memcpy( TempChunks, m_Chunks, sizeof( cChunk* ) * m_Size );
#else
for( unsigned int i = 0; i < a_NewSize; ++i )
TempChunks[i] = m_Chunks[i];
#endif
delete [] m_Chunks;
}
m_Chunks = TempChunks;
m_Allocated = a_NewSize;
}
void cChunkMap::cChunkNode::erase( cChunk* a_Chunk )
{
if( m_Size == 0 ) return;
cChunk** TempChunks = new cChunk*[m_Size];
unsigned int TempIdx = 0;
for( unsigned int i = 0; i < m_Size; ++i )
{
if( m_Chunks[i] != a_Chunk )
{
TempChunks[TempIdx] = m_Chunks[i];
TempIdx++;
}
}
delete [] m_Chunks;
m_Chunks = 0;
if( TempIdx > 0 )
{
m_Chunks = new cChunk*[ TempIdx ];
#ifdef USE_MEMCPY
memcpy( m_Chunks, TempChunks, sizeof( cChunk* ) * TempIdx );
#else
for( unsigned int i = 0; i < TempIdx; ++i )
m_Chunks[i] = TempChunks[i];
#endif
}
delete [] TempChunks;
m_Allocated = TempIdx;
m_Size = TempIdx;
}
cChunkMap::cChunkData* cChunkMap::cChunkLayer::GetChunk( int a_X, int a_Z )
{
const int LocalX = a_X - m_X * LAYER_SIZE;
const int LocalZ = a_Z - m_Z * LAYER_SIZE;
//LOG("LocalX:%i LocalZ:%i", LocalX, LocalZ );
if( LocalX < LAYER_SIZE && LocalZ < LAYER_SIZE && LocalX > -1 && LocalZ > -1 )
return &m_Chunks[ LocalX + LocalZ * LAYER_SIZE ];
return 0;
}
bool cChunkMap::RemoveLayer( cChunkLayer* a_Layer )
{
cChunkLayer* NewLayers = 0;
if( m_NumLayers > 1 )
NewLayers = new cChunkLayer[m_NumLayers-1];
int idx = 0;
bool bExcludedLayer = false;
for( int i = 0; i < m_NumLayers; ++i )
{
if( &m_Layers[i] != a_Layer )
{
if( idx < m_NumLayers-1 )
{
NewLayers[ idx ] = m_Layers[i];
idx++;
}
}
else
bExcludedLayer = true;
}
if( !bExcludedLayer )
{
LOGWARN("Could not remove layer, because layer was not found %i %i", a_Layer->m_X, a_Layer->m_Z);
delete [] NewLayers;
return false;
}
if( m_Layers ) delete [] m_Layers;
m_Layers = NewLayers;
m_NumLayers--;
return true;
}
cChunkMap::cChunkLayer* cChunkMap::AddLayer( const cChunkLayer & a_Layer )
{
cChunkLayer* TempLayers = new cChunkLayer[m_NumLayers+1];
if( m_NumLayers > 0 )
{
memcpy( TempLayers, m_Layers, sizeof( cChunkLayer ) * m_NumLayers );
delete [] m_Layers;
}
m_Layers = TempLayers;
m_Layers[m_NumLayers] = a_Layer;
cChunkLayer* NewLayer = &m_Layers[m_NumLayers];
m_NumLayers++;
return NewLayer;
}
void cChunkMap::AddChunk( cChunk* a_Chunk )
{
/* // OLD
m_Nodes[ MakeHash( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ) ].push_back( a_Chunk );
*/
// NEW
const int LayerX = (int)(floorf((float)a_Chunk->GetPosX() / (float)(LAYER_SIZE)));
const int LayerZ = (int)(floorf((float)a_Chunk->GetPosZ() / (float)(LAYER_SIZE)));
cChunkLayer* FoundLayer = GetLayer( LayerX, LayerZ );
if( !FoundLayer )
{
cChunkLayer NewLayer( LAYER_SIZE*LAYER_SIZE );
NewLayer.m_X = LayerX;
NewLayer.m_Z = LayerZ;
FoundLayer = AddLayer( NewLayer );
LOGWARN("Created new layer %i %i (total layers %i)", LayerX, LayerZ, m_NumLayers );
}
//Get local coordinates in layer
const int LocalX = a_Chunk->GetPosX() - LayerX * LAYER_SIZE;
const int LocalZ = a_Chunk->GetPosZ() - LayerZ * LAYER_SIZE;
if( FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_LiveChunk )
LOGWARN("WARNING: Added chunk to layer while it was already loaded!");
FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_LiveChunk = a_Chunk;
FoundLayer->m_NumChunksLoaded++;
}
void cChunkMap::RemoveChunk( cChunk* a_Chunk )
{
/* // OLD
m_Nodes[ MakeHash( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ) ].erase( a_Chunk );
*/
// NEW
cChunkLayer* Layer = GetLayerForChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() );
if( Layer )
{
cChunkData* Data = Layer->GetChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() );
if( Data->m_LiveChunk )
{
CompressChunk( Data );
Data->m_LiveChunk = 0; // Set live chunk to 0
}
Layer->m_NumChunksLoaded--;
}
}
void cChunkMap::CompressChunk( cChunkData* a_ChunkData )
{
if( a_ChunkData->m_LiveChunk )
{
// Delete already present compressed data
if( a_ChunkData->m_Compressed ) delete [] a_ChunkData->m_Compressed;
// Get Json data
Json::Value root;
std::string JsonData = "";
a_ChunkData->m_LiveChunk->SaveToJson( root );
if( !root.empty() )
{
Json::StyledWriter writer; // TODO FIXME: change to FastWriter ? :D
JsonData = writer.write( root );
}
unsigned int TotalSize = cChunk::c_BlockDataSize + JsonData.size();
uLongf CompressedSize = compressBound( TotalSize );
a_ChunkData->m_Compressed = new char[CompressedSize];
char* DataSource = a_ChunkData->m_LiveChunk->pGetBlockData();
if( JsonData.size() > 0 )
{
// Move stuff around, so data is aligned in memory
DataSource = new char[TotalSize];
memcpy( DataSource, a_ChunkData->m_LiveChunk->pGetBlockData(), cChunk::c_BlockDataSize );
memcpy( DataSource + cChunk::c_BlockDataSize, JsonData.c_str(), JsonData.size() );
}
int errorcode = compress2( (Bytef*)a_ChunkData->m_Compressed, &CompressedSize, (const Bytef*)DataSource, TotalSize, Z_DEFAULT_COMPRESSION);
if( errorcode != Z_OK )
{
LOGERROR("Error compressing data (%i)", errorcode );
}
a_ChunkData->m_CompressedSize = CompressedSize;
a_ChunkData->m_UncompressedSize = TotalSize;
if( DataSource != a_ChunkData->m_LiveChunk->pGetBlockData() )
delete [] DataSource;
}
}
unsigned int cChunkMap::MakeHash( int a_X, int a_Z )
{
const unsigned int HashX = abs( a_X ) % m_Width;
const unsigned int HashZ = abs( a_Z ) % m_Height;
return HashX + HashZ * m_Width;
}
cChunkMap::cChunkLayer* cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ )
{
const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE)));
const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE)));
return GetLayer( LayerX, LayerZ );
}
cChunkMap::cChunkLayer* cChunkMap::GetLayer( int a_LayerX, int a_LayerZ )
{
// Find layer in memory
for( int i = 0; i < m_NumLayers; ++i )
{
if( m_Layers[i].m_X == a_LayerX && m_Layers[i].m_Z == a_LayerZ )
{
return &m_Layers[i];
}
}
// Find layer on disk
cChunkLayer* Layer = LoadLayer( a_LayerX, a_LayerZ );
if( !Layer ) return 0;
cChunkLayer* NewLayer = AddLayer( *Layer );
delete Layer;
return NewLayer;
}
cChunk* cChunkMap::GetChunk( int a_X, int a_Y, int a_Z )
{
/* // OLD
unsigned int Hash = MakeHash( a_X, a_Z );
cChunkNode & Node = m_Nodes[ Hash ];
cChunk** Chunks = Node.GetChunks();
for( unsigned int i = 0; i < Node.size(); ++i )
{
if( Chunks[i]->GetPosX() == a_X && // Check if we found the right chunk
Chunks[i]->GetPosY() == a_Y &&
Chunks[i]->GetPosZ() == a_Z )
{
return Chunks[i];
}
}
*/
// NEW
cChunkLayer* Layer = GetLayerForChunk( a_X, a_Z );
if( Layer )
{
cChunkData* Data = Layer->GetChunk( a_X, a_Z );
if( Data->m_LiveChunk ) return Data->m_LiveChunk;
// Decompress cached chunk
if( Data->m_Compressed )
{
uLongf DestSize = Data->m_UncompressedSize;
char* BlockData = new char[ DestSize ];
int errorcode = uncompress( (Bytef*)BlockData, &DestSize, (Bytef*)Data->m_Compressed, Data->m_CompressedSize );
if( Data->m_UncompressedSize != DestSize )
{
LOGWARN("Lulwtf, expected uncompressed size differs!");
delete [] BlockData;
}
else if( errorcode != Z_OK )
{
LOGERROR("ERROR: Decompressing chunk data! %i", errorcode );
switch( errorcode )
{
case Z_MEM_ERROR:
LOGERROR("Not enough memory");
break;
case Z_BUF_ERROR:
LOGERROR("Not enough room in output buffer");
break;
case Z_DATA_ERROR:
LOGERROR("Input data corrupted or incomplete");
break;
default:
break;
};
delete [] BlockData;
}
else
{
cChunk* Chunk = new cChunk(a_X, a_Y, a_Z);
memcpy( Chunk->m_BlockData, BlockData, cChunk::c_BlockDataSize );
Chunk->CalculateHeightmap();
Data->m_LiveChunk = Chunk;
Layer->m_NumChunksLoaded++;
if( DestSize > cChunk::c_BlockDataSize ) // We gots some extra data :D
{
LOGINFO("Parsing trailing JSON");
Json::Value root; // will contains the root value after parsing.
Json::Reader reader;
if( !reader.parse( BlockData + cChunk::c_BlockDataSize, root, false ) )
{
LOGERROR("Failed to parse trailing JSON!");
}
else
{
//Json::StyledWriter writer;
//LOGINFO("Trailing Json:" );
//printf("%s", writer.write( root ).c_str() );
Chunk->LoadFromJson( root );
}
}
delete [] BlockData;
delete [] Data->m_Compressed; Data->m_Compressed = 0; Data->m_CompressedSize = 0;
return Chunk;
}
}
}
return 0;
}
void cChunkMap::Tick( float a_Dt )
{
/* // OLD
for( int i = 0; i < m_Width*m_Height; ++i )
{
cChunkNode & Node = m_Nodes[ i ];
cChunk** Chunks = Node.GetChunks();
for( unsigned int i = 0; i < Node.size(); ++i )
{
Chunks[i]->Tick( a_Dt );
}
}
*/
// NEW
for( int l = 0; l < m_NumLayers; ++l )
{
for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i )
{
cChunk* Chunk = m_Layers[l].m_Chunks[i].m_LiveChunk;
if( Chunk )
Chunk->Tick( a_Dt );
}
}
}
void cChunkMap::UnloadUnusedChunks()
{
cWorld* World = cRoot::Get()->GetWorld();
/* // OLD
for( int i = 0; i < m_Width*m_Height; ++i )
{
cChunkNode & Node = m_Nodes[ i ];
cChunk** Chunks = Node.GetChunks();
for( unsigned int i = 0; i < Node.size(); ++i )
{
if( Chunks[i]->GetClients().size() == 0 )
{
Chunks[i]->SaveToDisk();
LOG("Unloading %p", Chunks[i] );
World->RemoveSpread( Chunks[i] );
cChunk* TheChunk = Chunks[i];
RemoveChunk( TheChunk );
delete TheChunk;
//Node.erase( Chunks[i] );
Chunks = Node.GetChunks(); // Chunks pointer is no longer valid, get a new one
}
}
}
*/
// NEW
for( int l = 0; l < m_NumLayers; ++l )
{
cChunkLayer & Layer = m_Layers[l];
for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i )
{
cChunk* Chunk = Layer.m_Chunks[i].m_LiveChunk;
if( Chunk && Chunk->GetClients().size() == 0 )
{
Chunk->SaveToDisk();
World->RemoveSpread( Chunk );
RemoveChunk( Chunk );
delete Chunk;
}
}
// Unload layers
if( Layer.m_NumChunksLoaded == 0 )
{
SaveLayer( &Layer );
for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) // Free all chunk data for layer
{
if( Layer.m_Chunks[i].m_Compressed )
delete [] Layer.m_Chunks[i].m_Compressed;
if( Layer.m_Chunks[i].m_LiveChunk )
delete Layer.m_Chunks[i].m_LiveChunk;
}
if( RemoveLayer( &Layer ) ) l--;
}
else if( Layer.m_NumChunksLoaded < 0 )
{
LOGERROR("WTF! Chunks loaded in layer is %i !!", Layer.m_NumChunksLoaded );
}
}
}
bool cChunkMap::RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom /* = 0 */ )
{
/* // OLD
for( int i = 0; i < m_Width*m_Height; ++i )
{
cChunkNode & Node = m_Nodes[ i ];
cChunk** Chunks = Node.GetChunks();
for( unsigned int i = 0; i < Node.size(); ++i )
{
if( Chunks[i] != a_CalledFrom )
{
if( Chunks[i]->RemoveEntity( a_Entity, a_CalledFrom ) )
return true;
}
}
}
*/
// NEW
for( int i = 0; i < m_NumLayers; ++i )
{
cChunkLayer & Layer = m_Layers[i];
for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i )
{
cChunk* Chunk = Layer.m_Chunks[i].m_LiveChunk;
if( Chunk != a_CalledFrom )
{
if( Chunk && Chunk->RemoveEntity( a_Entity, a_CalledFrom ) )
return true;
}
}
}
LOG("WARNING: Entity was not found in any chunk!");
return false;
}
void cChunkMap::SaveAllChunks()
{
for( int i = 0; i < m_Width*m_Height; ++i )
{
cChunkNode & Node = m_Nodes[ i ];
cChunk** Chunks = Node.GetChunks();
for( unsigned int i = 0; i < Node.size(); ++i )
{
Chunks[i]->SaveToDisk();
}
}
for( int i = 0; i < m_NumLayers; ++i )
{
SaveLayer( &m_Layers[i] );
}
}
/********************************
* Saving and loading
**/
void cChunkMap::SaveLayer( cChunkLayer* a_Layer )
{
cMakeDir::MakeDir("world");
char SourceFile[128];
#ifdef _WIN32
sprintf_s(SourceFile, 128, "world/X%i_Z%i.pak", a_Layer->m_X, a_Layer->m_Z );
#else
sprintf(SourceFile, "world/X%i_Z%i.pak", a_Layer->m_X, a_Layer->m_Z );
#endif
FILE* f = 0;
#ifdef _WIN32
if( fopen_s(&f, SourceFile, "wb" ) == 0 ) // no error
#else
if( (f = fopen(SourceFile, "wb" )) != 0 ) // no error
#endif
{
//---------------
// Header
char PakVersion = 1;
char ChunkVersion = 1;
fwrite( &PakVersion, sizeof(PakVersion), 1, f ); // pak version
fwrite( &ChunkVersion, sizeof(ChunkVersion), 1, f ); // chunk version
// Count number of chunks in layer
short NumChunks = 0;
for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i )
{
if( a_Layer->m_Chunks[i].m_Compressed || a_Layer->m_Chunks[i].m_LiveChunk )
NumChunks++;
}
fwrite( &NumChunks, sizeof( NumChunks ), 1, f );
LOG("Num Chunks in layer: %i", NumChunks );
//---------------
// Chunk headers
for( int z = 0; z < LAYER_SIZE; ++z )
{
for( int x = 0; x < LAYER_SIZE; ++x )
{
cChunkData & Data = a_Layer->m_Chunks[x + z*LAYER_SIZE];
CompressChunk( &Data );
if( Data.m_Compressed || Data.m_LiveChunk )
{
int ChunkX = a_Layer->m_X*LAYER_SIZE + x;
int ChunkZ = a_Layer->m_Z*LAYER_SIZE + z;
unsigned int Size = Data.m_CompressedSize; // Needs to be size of compressed data
unsigned int USize = Data.m_UncompressedSize; // Uncompressed size
fwrite( &ChunkX, sizeof( ChunkX ), 1, f );
fwrite( &ChunkZ, sizeof( ChunkZ ), 1, f );
fwrite( &Size, sizeof( Size ), 1, f );
fwrite( &USize, sizeof( USize ), 1, f );
}
}
}
//----------------
// Chunk data
for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i )
{
char* Compressed = a_Layer->m_Chunks[i].m_Compressed;
if( Compressed )
{
fwrite( Compressed, a_Layer->m_Chunks[i].m_CompressedSize, 1, f );
if(a_Layer->m_Chunks[i].m_LiveChunk) // If there's a live chunk we have no need for compressed data
{
delete [] a_Layer->m_Chunks[i].m_Compressed;
a_Layer->m_Chunks[i].m_Compressed = 0;
a_Layer->m_Chunks[i].m_CompressedSize = 0;
}
}
}
fclose(f);
}
else
{
LOGERROR("ERROR: Could not write to file %s", SourceFile );
}
}
cChunkMap::cChunkLayer* cChunkMap::LoadLayer(int a_LayerX, int a_LayerZ )
{
char SourceFile[128];
#ifdef _WIN32
sprintf_s(SourceFile, 128, "world/X%i_Z%i.pak", a_LayerX, a_LayerZ );
#else
sprintf(SourceFile, "world/X%i_Z%i.pak", a_LayerX, a_LayerZ );
#endif
FILE* f = 0;
#ifdef _WIN32
if( fopen_s(&f, SourceFile, "rb" ) == 0 ) // no error
#else
if( (f = fopen(SourceFile, "rb" )) != 0 ) // no error
#endif
{
char PakVersion = 0;
char ChunkVersion = 0;
if( fread( &PakVersion, sizeof(PakVersion), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( PakVersion != 1 ) { LOGERROR("WRONG PAK VERSION!"); fclose(f); return 0; }
if( fread( &ChunkVersion, sizeof(ChunkVersion), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( PakVersion != 1 ) { LOGERROR("WRONG CHUNK VERSION!"); fclose(f); return 0; }
short NumChunks = 0;
if( fread( &NumChunks, sizeof(NumChunks), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
LOG("Num chunks: %i", NumChunks );
cChunkLayer* Layer = new cChunkLayer( LAYER_SIZE*LAYER_SIZE );
Layer->m_X = a_LayerX;
Layer->m_Z = a_LayerZ;
cChunkData** OrderedData = new cChunkData*[ NumChunks ]; // So we can loop over the chunks in the order they were loaded
// Loop over all chunk headers
for( short i = 0; i < NumChunks; ++i )
{
int ChunkX = 0;
int ChunkZ = 0;
if( fread( &ChunkX, sizeof(ChunkX), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &ChunkZ, sizeof(ChunkZ), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
cChunkData* Data = Layer->GetChunk( ChunkX, ChunkZ );
if( Data )
{
if( fread( &Data->m_CompressedSize, sizeof(Data->m_CompressedSize), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &Data->m_UncompressedSize, sizeof(Data->m_UncompressedSize), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
}
else
{
LOGERROR("Chunk with wrong coordinates in pak file! %i %i", ChunkX, ChunkZ );
fclose(f);
return 0;
}
OrderedData[i] = Data;
}
// Loop over chunks again, in the order they were loaded, and load their compressed data
for( short i = 0; i < NumChunks; ++i )
{
cChunkData* Data = OrderedData[i];
Data->m_Compressed = new char[ Data->m_CompressedSize ];
if( fread( Data->m_Compressed, Data->m_CompressedSize, 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
/* // Some testing...
uLongf DestSize = Data->m_UncompressedSize;
char* BlockData = new char[ DestSize ];
int errorcode = uncompress( (Bytef*)BlockData, &DestSize, (Bytef*)Data->m_Compressed, Data->m_CompressedSize );
if( errorcode != Z_OK )
{
LOGERROR("lulwut");
}
*/
}
delete [] OrderedData;
fclose(f);
return Layer;
}
else
{
//LOGWARN("Could not open file %s", SourceFile );
}
return 0;
}

91
source/cChunkMap.h Normal file
View File

@ -0,0 +1,91 @@
#pragma once
class cEntity;
class cChunk;
class cChunkMap
{
public:
cChunkMap( int a_Width, int a_Height );
~cChunkMap();
void AddChunk( cChunk* a_Chunk );
unsigned int MakeHash( int a_X, int a_Z );
cChunk* GetChunk( int a_X, int a_Y, int a_Z );
void RemoveChunk( cChunk* a_Chunk );
void Tick( float a_Dt );
void UnloadUnusedChunks();
bool RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom = 0 );
void SaveAllChunks();
private:
class cChunkData
{
public:
cChunkData()
: m_Compressed( 0 )
, m_LiveChunk( 0 )
, m_CompressedSize( 0 )
, m_UncompressedSize( 0 )
{}
char* m_Compressed;
unsigned int m_CompressedSize;
unsigned int m_UncompressedSize;
cChunk* m_LiveChunk;
};
class cChunkLayer
{
public:
cChunkLayer()
: m_Chunks( 0 )
, m_X( 0 )
, m_Z( 0 )
, m_NumChunksLoaded( 0 )
{}
cChunkLayer( int a_NumChunks )
: m_Chunks( new cChunkData[a_NumChunks] )
, m_X( 0 )
, m_Z( 0 )
, m_NumChunksLoaded( 0 )
{}
cChunkData* GetChunk( int a_X, int a_Z );
cChunkData* m_Chunks;
int m_X, m_Z;
int m_NumChunksLoaded;
};
void SaveLayer( cChunkLayer* a_Layer );
cChunkLayer* LoadLayer( int a_LayerX, int a_LayerZ );
cChunkLayer* GetLayerForChunk( int a_ChunkX, int a_ChunkZ );
cChunkLayer* GetLayer( int a_LayerX, int a_LayerZ );
cChunkLayer* AddLayer( const cChunkLayer & a_Layer );
bool RemoveLayer( cChunkLayer* a_Layer );
void CompressChunk( cChunkData* a_ChunkData );
int m_NumLayers;
cChunkLayer* m_Layers;
class cChunkNode
{
public:
cChunkNode();
~cChunkNode();
void push_back( cChunk* a_Chunk );
unsigned int size() { return m_Size; }
unsigned int allocated() { return m_Allocated; }
void resize( unsigned int a_NewSize );
void erase( cChunk* a_Chunk );
cChunk** GetChunks() { return m_Chunks; }
private:
unsigned int m_Size;
unsigned int m_Allocated;
cChunk** m_Chunks;
};
cChunkNode* m_Nodes;
int m_Width, m_Height;
};

1082
source/cClientHandle.cpp Normal file

File diff suppressed because it is too large Load Diff

70
source/cClientHandle.h Normal file
View File

@ -0,0 +1,70 @@
#pragma once
class cSocket;
class cSemaphore;
class cEvent;
class Game;
class cPacket;
class cChunk;
class cPlayer;
class cClientHandle // tolua_export
{ // tolua_export
public:
enum ENUM_PRIORITY
{
E_PRIORITY_LOW,
E_PRIORITY_NORMAL
};
cClientHandle(const cSocket & a_Socket);
~cClientHandle();
static const int VIEWDISTANCE = 13;
const cSocket & GetSocket();
cPlayer* GetPlayer() { return m_Player; } // tolua_export
void Kick( const char* a_Reason ); //tolua_export
void AddPacket( cPacket * a_Packet );
void HandlePendingPackets();
void StreamChunks();
void StreamChunksSmart( cChunk** a_Chunks, unsigned int a_NumChunks );
inline void SetLoggedIn( bool a_bLoggedIn ) { m_bLoggedIn = a_bLoggedIn; }
inline bool IsLoggedIn() { return m_bLoggedIn; }
void Tick(float a_Dt);
bool IsDestroyed() { return m_bDestroyed; }
void Destroy();
cChunk* m_LoadedChunks[VIEWDISTANCE*VIEWDISTANCE];
void Send( const cPacket & a_Packet, ENUM_PRIORITY a_Priority = E_PRIORITY_NORMAL );
static void SendThread( void *lpParam );
static void ReceiveThread( void *lpParam );
static void AuthenticateThread( void* a_Param );
const char* GetUsername();
private:
void HandlePacket( cPacket* a_Packet );
void RemovePacket( cPacket * a_Packet );
void SendLoginResponse();
struct sClientHandleState;
sClientHandleState* m_pState;
bool m_bDestroyed;
cPlayer* m_Player;
bool m_bKicking;
float m_TimeLastPacket;
bool m_bLoggedIn;
bool m_bKeepThreadGoing;
}; // tolua_export

103
source/cCraftingWindow.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "cCraftingWindow.h"
#include "cItem.h"
#include "cMCLogger.h"
#include "cRecipeChecker.h"
#include "cPlayer.h"
#include "cClientHandle.h"
#include "cInventory.h"
#include "cPickup.h"
#include "cRoot.h"
#include "packets/cPacket_WindowClick.h"
#include "packets/cPacket_InventorySlot.h"
cCraftingWindow::cCraftingWindow( cWindowOwner* a_Owner, bool a_bInventoryVisible )
: cWindow( a_Owner, a_bInventoryVisible )
{
SetWindowID( 1 );
SetWindowType( 1 ); // Workbench
cItem* Slots = new cItem[10];
SetSlots( Slots, 10 );
}
void cCraftingWindow::Clicked( cPacket_WindowClick* a_ClickPacket, cPlayer & a_Player )
{
bool bDontCook = false;
// Override for craft result slot
if( a_ClickPacket->m_SlotNum == 0 )
{
LOG("In craft slot: %i x %i !!", GetSlot(0)->m_ItemID, GetSlot(0)->m_ItemCount );
cItem* DraggingItem = GetDraggingItem( &a_Player );
if( DraggingItem->m_ItemID <= 0 )
{
*DraggingItem = *GetSlot(0);
GetSlot(0)->Empty();
}
else if( DraggingItem->Equals( *GetSlot(0) ) )
{
if( DraggingItem->m_ItemCount + GetSlot(0)->m_ItemCount <= 64 )
{
DraggingItem->m_ItemCount += GetSlot(0)->m_ItemCount;
GetSlot(0)->Empty();
}
else
{
bDontCook = true;
}
}
else
{
bDontCook = true;
}
LOG("Dragging Dish %i", DraggingItem->m_ItemCount );
}
else
{
cWindow::Clicked( a_ClickPacket, a_Player );
}
if( a_ClickPacket->m_SlotNum >= 0 && a_ClickPacket->m_SlotNum < 10 )
{
cItem CookedItem;
if( a_ClickPacket->m_SlotNum == 0 && !bDontCook )
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( GetSlots()+1, 3, 3, true );
}
else
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( GetSlots()+1, 3, 3 );
}
*GetSlot(0) = CookedItem;
LOG("You cooked: %i x %i !!", GetSlot(0)->m_ItemID, GetSlot(0)->m_ItemCount );
}
SendWholeWindow( a_Player.GetClientHandle() );
a_Player.GetInventory().SendWholeInventory( a_Player.GetClientHandle() );
// Separate packet for result =/ Don't know why
cPacket_InventorySlot Packet;
Packet.m_WindowID = (char)GetWindowID();
Packet.m_SlotNum = 0;
Packet.m_ItemID = (short)GetSlot(0)->m_ItemID;
Packet.m_ItemCount = GetSlot(0)->m_ItemCount;
Packet.m_ItemUses = (char)GetSlot(0)->m_ItemHealth;
a_Player.GetClientHandle()->Send( Packet );
}
void cCraftingWindow::Close( cPlayer & a_Player )
{
// Start from slot 1, don't drop what's in the result slot
for( int i = 1; i < GetNumSlots(); i++ )
{
cItem* Item = GetSlot( i );
if( Item->m_ItemID > 0 && Item->m_ItemCount > 0 )
{
float vX = 0, vY = 0, vZ = 0;
EulerToVector( -a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY );
vY = -vY*2 + 1.f;
cPickup* Pickup = new cPickup( (int)(a_Player.GetPosX()*32), (int)(a_Player.GetPosY()*32) + (int)(1.6f*32), (int)(a_Player.GetPosZ()*32), *Item, vX*2, vY*2, vZ*2 );
Pickup->Initialize();
}
Item->Empty();
}
cWindow::Close( a_Player );
}

13
source/cCraftingWindow.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "cWindow.h"
class cWindowOwner;
class cCraftingWindow : public cWindow
{
public:
cCraftingWindow( cWindowOwner* a_Owner, bool a_bInventoryVisible );
virtual void Clicked( cPacket_WindowClick* a_ClickPacket, cPlayer & a_Player );
virtual void Close( cPlayer & a_Player );
};

View File

@ -0,0 +1,60 @@
#include "cCriticalSection.h"
#include "cMCLogger.h"
#ifdef _WIN32
#include <Windows.h>
#else
#include <pthread.h>
#endif
cCriticalSection::cCriticalSection()
{
#ifdef _WIN32
m_CriticalSectionPtr = new CRITICAL_SECTION;
InitializeCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr );
#else
m_Attributes = new pthread_mutexattr_t;
pthread_mutexattr_init((pthread_mutexattr_t*)m_Attributes);
pthread_mutexattr_settype((pthread_mutexattr_t*)m_Attributes, PTHREAD_MUTEX_RECURSIVE);
m_CriticalSectionPtr = new pthread_mutex_t;
if( pthread_mutex_init( (pthread_mutex_t*)m_CriticalSectionPtr, (pthread_mutexattr_t*)m_Attributes ) != 0 )
{
LOG("ERROR: Could not initialize Critical Section!");
}
#endif
}
cCriticalSection::~cCriticalSection()
{
#ifdef _WIN32
DeleteCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr );
delete (CRITICAL_SECTION*)m_CriticalSectionPtr;
#else
if( pthread_mutex_destroy( (pthread_mutex_t*)m_CriticalSectionPtr ) != 0 )
{
LOG("ERROR: Could not destroy Critical Section!");
}
delete (pthread_mutex_t*)m_CriticalSectionPtr;
pthread_mutexattr_destroy( (pthread_mutexattr_t*)m_Attributes );
delete (pthread_mutexattr_t*)m_Attributes;
#endif
}
void cCriticalSection::Lock()
{
#ifdef _WIN32
EnterCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr );
#else
pthread_mutex_lock( (pthread_mutex_t*)m_CriticalSectionPtr );
#endif
}
void cCriticalSection::Unlock()
{
#ifdef _WIN32
LeaveCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr );
#else
pthread_mutex_unlock( (pthread_mutex_t*)m_CriticalSectionPtr );
#endif
}

16
source/cCriticalSection.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
class cCriticalSection
{
public:
cCriticalSection();
~cCriticalSection();
void Lock();
void Unlock();
private:
void* m_CriticalSectionPtr; // Pointer to a CRITICAL_SECTION object
#ifndef _WIN32
void* m_Attributes;
#endif
};

10
source/cCuboid.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "cCuboid.h"
#include <algorithm> // swap
void cCuboid::Sort()
{
if( p1.x > p2.x ) std::swap( p1.x, p2.x );
if( p1.y > p2.y ) std::swap( p1.y, p2.y );
if( p1.z > p2.z ) std::swap( p1.z, p2.z );
}

40
source/cCuboid.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "Vector3i.h"
#include "Vector3d.h"
class cCuboid //tolua_export
{ //tolua_export
public: //tolua_export
cCuboid() {} //tolua_export
cCuboid( const cCuboid & a_Cuboid ) : p1( a_Cuboid.p1 ), p2( a_Cuboid.p2 ) {} //tolua_export
cCuboid( const Vector3i & a_p1, const Vector3i & a_p2 ) : p1( a_p1 ), p2( a_p2 ) {} //tolua_export
Vector3i p1, p2; //tolua_export
void Sort(); //tolua_export
bool IsInside( const Vector3i & v ) const //tolua_export
{ //tolua_export
if( v.x >= p1.x && v.x <= p2.x
&& v.y >= p1.y && v.y <= p2.y
&& v.z >= p1.z && v.z <= p2.z )
{
return true;
}
return false;
} //tolua_export
bool IsInside( const Vector3d & v ) const //tolua_export
{ //tolua_export
if( v.x >= p1.x && v.x <= p2.x
&& v.y >= p1.y && v.y <= p2.y
&& v.z >= p1.z && v.z <= p2.z )
{
return true;
}
return false;
} //tolua_export
}; //tolua_export

283
source/cEntity.cpp Normal file
View File

@ -0,0 +1,283 @@
#include "cEntity.h"
#include "cWorld.h"
#include "cChunk.h"
#include "cMCLogger.h"
#include "cServer.h"
#include "cRoot.h"
#include "Vector3d.h"
#include "Vector3f.h"
#include "Matrix4f.h"
#include "cReferenceManager.h"
#include "cClientHandle.h"
#include "packets/cPacket_DestroyEntity.h"
int cEntity::m_EntityCount = 0;
cEntity::cEntity(const double & a_X, const double & a_Y, const double & a_Z)
: m_UniqueID( 0 )
, m_Referencers( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCERS ) )
, m_References( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCES ) )
, m_ChunkX( 0 )
, m_ChunkY( 0 )
, m_ChunkZ( 0 )
, m_Pos( new Vector3d( a_X, a_Y, a_Z ) )
, m_bDirtyPosition( true )
, m_Rot( new Vector3f() )
, m_bDirtyOrientation( true )
, m_bDestroyed( false )
, m_EntityType( E_ENTITY )
{
m_EntityCount++;
m_UniqueID = m_EntityCount;
}
cEntity::~cEntity()
{
delete m_Referencers;
delete m_References;
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if( Chunk )
{
cPacket_DestroyEntity DestroyEntity( this );
Chunk->Broadcast( DestroyEntity );
Chunk->RemoveEntity( *this );
}
delete m_Pos;
delete m_Rot;
}
void cEntity::Initialize()
{
cRoot::Get()->GetWorld()->AddEntity( this );
cWorld::BlockToChunk( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z, m_ChunkX, m_ChunkY, m_ChunkZ );
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
if( Chunk )
{
//LOG("Adding entity %i to chunk %i %i %i", m_UniqueID, Chunk->GetPosX(), Chunk->GetPosY(), Chunk->GetPosZ() );
Chunk->AddEntity( *this );
}
}
void cEntity::WrapRotation()
{
while(m_Rot->x > 180.f) m_Rot->x-=360.f; // Wrap it
while(m_Rot->x < -180.f) m_Rot->x+=360.f;
while(m_Rot->y > 180.f) m_Rot->y-=360.f;
while(m_Rot->y < -180.f) m_Rot->y+=360.f;
}
void cEntity::MoveToCorrectChunk()
{
int ChunkX = 0, ChunkY = 0, ChunkZ = 0;
cWorld::BlockToChunk( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z, ChunkX, ChunkY, ChunkZ );
if( m_ChunkX != ChunkX || m_ChunkY != ChunkY || m_ChunkZ != ChunkZ )
{
cWorld* World = cRoot::Get()->GetWorld();
LOG("From %i %i To %i %i", m_ChunkX, m_ChunkZ, ChunkX, ChunkZ );
cChunk* Chunk = World->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
typedef std::list< cClientHandle* > ClientList;
ClientList BeforeClients;
if( Chunk )
{
Chunk->RemoveEntity( *this );
BeforeClients = Chunk->GetClients();
}
m_ChunkX = ChunkX; m_ChunkY = ChunkY; m_ChunkZ = ChunkZ;
cChunk* NewChunk = World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
ClientList AfterClients;
if( NewChunk )
{
NewChunk->AddEntity( *this );
AfterClients = NewChunk->GetClients();
}
/********************
* I reaalllyyyy don't like this piece of code, but it's needed I guess (maybe there's a way to optimize this)
**/
// Now compare clients before with after
for( ClientList::iterator itr = BeforeClients.begin(); itr != BeforeClients.end(); ++itr )
{
bool bFound = false;
for( ClientList::iterator itr2 = AfterClients.begin(); itr2 != AfterClients.end(); ++itr2 )
{
if( *itr2 == *itr )
{
bFound = true;
break;
}
}
if( !bFound ) // Client was in old chunk, but not new, so destroy on that client
{
cPacket_DestroyEntity DestroyEntity( this );
(*itr)->Send( DestroyEntity );
}
}
// Now compare clients after with before
for( ClientList::iterator itr = AfterClients.begin(); itr != AfterClients.end(); ++itr )
{
bool bFound = false;
for( ClientList::iterator itr2 = BeforeClients.begin(); itr2 != BeforeClients.end(); ++itr2 )
{
if( *itr2 == *itr )
{
bFound = true;
break;
}
}
if( !bFound ) // Client is in the new chunk, but not in old, so spawn on the client
{
SpawnOn( *itr );
}
}
}
}
CLASS_DEF_GETCLASS( cEntity );
bool cEntity::IsA( const char* a_EntityType )
{
//LOG("IsA( cEntity ) : %s", a_EntityType);
if( strcmp( a_EntityType, "cEntity" ) == 0 ) return true;
return false;
}
//////////////////////////////////////////////////////////////////////////
// Set orientations
void cEntity::SetRot( const Vector3f & a_Rot )
{
*m_Rot = a_Rot;
m_bDirtyOrientation = true;
}
void cEntity::SetRotation( float a_Rotation )
{
m_Rot->x = a_Rotation;
m_bDirtyOrientation = true;
}
void cEntity::SetPitch( float a_Pitch )
{
m_Rot->y = a_Pitch;
m_bDirtyOrientation = true;
}
void cEntity::SetRoll( float a_Roll )
{
m_Rot->z = a_Roll;
m_bDirtyOrientation = true;
}
//////////////////////////////////////////////////////////////////////////
// Get orientations
const Vector3f & cEntity::GetRot()
{
return *m_Rot;
}
float cEntity::GetRotation()
{
return m_Rot->x;
}
float cEntity::GetPitch()
{
return m_Rot->y;
}
float cEntity::GetRoll()
{
return m_Rot->z;
}
//////////////////////////////////////////////////////////////////////////
// Get look vector (this is NOT a rotation!)
Vector3f cEntity::GetLookVector()
{
Matrix4f m;
m.Init( Vector3f(), 0, m_Rot->x, -m_Rot->y );
Vector3f Look = m.Transform( Vector3f(0, 0, 1) );
LOG("Look: %0.1f %0.1f %0.1f", Look.x, Look.y, Look.z );
return Look;
}
//////////////////////////////////////////////////////////////////////////
// Set position
void cEntity::SetPosition( const Vector3d & a_Pos )
{
*m_Pos = a_Pos;
MoveToCorrectChunk();
m_bDirtyPosition = true;
}
void cEntity::SetPosition( const double & a_PosX, const double & a_PosY, const double & a_PosZ )
{
m_Pos->Set( a_PosX, a_PosY, a_PosZ );
MoveToCorrectChunk();
m_bDirtyPosition = true;
}
void cEntity::SetPosX( const double & a_PosX )
{
m_Pos->x = a_PosX;
MoveToCorrectChunk();
m_bDirtyPosition = true;
}
void cEntity::SetPosY( const double & a_PosY )
{
m_Pos->y = a_PosY;
MoveToCorrectChunk();
m_bDirtyPosition = true;
}
void cEntity::SetPosZ( const double & a_PosZ )
{
m_Pos->z = a_PosZ;
MoveToCorrectChunk();
m_bDirtyPosition = true;
}
//////////////////////////////////////////////////////////////////////////
// Get position
const Vector3d & cEntity::GetPosition()
{
return *m_Pos;
}
const double & cEntity::GetPosX()
{
return m_Pos->x;
}
const double & cEntity::GetPosY()
{
return m_Pos->y;
}
const double & cEntity::GetPosZ()
{
return m_Pos->z;
}
//////////////////////////////////////////////////////////////////////////
// Reference stuffs
void cEntity::AddReference( cEntity*& a_EntityPtr )
{
m_References->AddReference( a_EntityPtr );
a_EntityPtr->ReferencedBy( a_EntityPtr );
}
void cEntity::ReferencedBy( cEntity*& a_EntityPtr )
{
m_Referencers->AddReference( a_EntityPtr );
}
void cEntity::Dereference( cEntity*& a_EntityPtr )
{
m_Referencers->Dereference( a_EntityPtr );
}

108
source/cEntity.h Normal file
View File

@ -0,0 +1,108 @@
#pragma once
#include "MemoryLeak.h"
#define CLASS_PROT_ISA() virtual bool IsA( const char* a_EntityType );
#define CLASS_PROT_GETCLASS() virtual const char* GetClass();
/* Can't use this (yet) because of tolua */
#define CLASS_PROTOTYPE() \
CLASS_PROT_ISA(); \
CLASS_PROT_GETCLASS();
#define CLASS_DEF_ISA( classname, superclass ) \
bool classname::IsA( const char* a_EntityType ) \
{ \
if( strcmp( a_EntityType, #classname ) == 0 ) return true; \
return superclass::IsA( a_EntityType ); \
}
#define CLASS_DEF_GETCLASS( classname ) \
const char* classname::GetClass() \
{ \
return #classname; \
}
#define CLASS_DEFINITION( classname, superclass ) \
CLASS_DEF_ISA( classname, superclass ) \
CLASS_DEF_GETCLASS( classname )
class cReferenceManager;
class Vector3d;
class Vector3f;
class cClientHandle;
class cEntity //tolua_export
{ //tolua_export
public: //tolua_export
cEntity(const double & a_X, const double & a_Y, const double & a_Z); //tolua_export
virtual ~cEntity(); //tolua_export
virtual void Initialize(); //tolua_export
enum ENUM_ENTITY_TYPE //tolua_export
{ //tolua_export
E_ENTITY, //tolua_export
E_PLAYER, //tolua_export
E_PICKUP //tolua_export
}; //tolua_export
virtual unsigned int GetEntityType() { return m_EntityType; } //tolua_export
virtual bool IsA( const char* a_EntityType ); //tolua_export
virtual const char* GetClass(); //tolua_export
const Vector3d & GetPosition(); //tolua_export
const double & GetPosX(); //tolua_export
const double & GetPosY(); //tolua_export
const double & GetPosZ(); //tolua_export
const Vector3f & GetRot(); //tolua_export
float GetRotation(); //tolua_export
float GetPitch(); //tolua_export
float GetRoll(); //tolua_export
Vector3f GetLookVector(); //tolua_export
void SetPosX( const double & a_PosX ); //tolua_export
void SetPosY( const double & a_PosY ); //tolua_export
void SetPosZ( const double & a_PosZ ); //tolua_export
void SetPosition( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export
void SetPosition( const Vector3d & a_Pos ); //tolua_export
void SetRot( const Vector3f & a_Rot ); //tolua_export
void SetRotation( float a_Rotation ); //tolua_export
void SetPitch( float a_Pitch ); //tolua_export
void SetRoll( float a_Roll ); //tolua_export
inline int GetUniqueID() { return m_UniqueID; } //tolua_export
inline bool IsDestroyed() { return m_bDestroyed; } //tolua_export
void Destroy() { m_bDestroyed = true; } //tolua_export
virtual void Tick(float a_Dt) = 0; //tolua_export
virtual void SpawnOn( cClientHandle* a_Target ) = 0; //tolua_export
void WrapRotation();
protected:
void MoveToCorrectChunk();
friend class cReferenceManager;
void AddReference( cEntity*& a_EntityPtr );
void ReferencedBy( cEntity*& a_EntityPtr );
void Dereference( cEntity*& a_EntityPtr );
static int m_EntityCount;
int m_UniqueID;
cReferenceManager* m_Referencers;
cReferenceManager* m_References;
int m_ChunkX, m_ChunkY, m_ChunkZ;
Vector3d* m_Pos;
bool m_bDirtyPosition;
Vector3f* m_Rot;
bool m_bDirtyOrientation;
bool m_bDestroyed;
ENUM_ENTITY_TYPE m_EntityType;
}; //tolua_export

112
source/cEvent.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "cEvent.h"
#include "cMCLogger.h"
#ifdef _WIN32
#include <Windows.h>
#else
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#endif
cEvent::cEvent( unsigned int a_NumEvents /* = 1 */ )
: m_NumEvents( a_NumEvents )
#ifndef _WIN32
, m_bNamed( false )
#endif
{
if( m_NumEvents < 1 ) m_NumEvents = 1;
#ifdef _WIN32
m_Handle = new HANDLE[ m_NumEvents ];
for( unsigned int i = 0; i < m_NumEvents; i++)
{
((HANDLE*)m_Handle)[i] = CreateEvent( 0, FALSE, FALSE, 0 );
}
#else
m_Handle = new sem_t*[ m_NumEvents ];
for( unsigned int i = 0; i < m_NumEvents; i++)
{
sem_t* & HandlePtr = ((sem_t**)m_Handle)[i];
HandlePtr = new sem_t;
if( sem_init( HandlePtr, 0, 0 ) )
{
LOG("WARNING cEvent: Could not create unnamed semaphore, fallback to named.");
m_bNamed = true;
delete HandlePtr; // named semaphores return their own address
char c_Str[32];
sprintf( c_Str, "cEvent%p", &HandlePtr );
HandlePtr = sem_open( c_Str, O_CREAT, 777, 0 );
if( HandlePtr == SEM_FAILED )
LOG("ERROR: Could not create Event. (%i)", errno);
else
if( sem_unlink( c_Str ) != 0 )
LOG("ERROR: Could not unlink cEvent. (%i)", errno);
}
}
#endif
}
cEvent::~cEvent()
{
#ifdef _WIN32
for( unsigned int i = 0; i < m_NumEvents; i++ )
{
CloseHandle( ((HANDLE*)m_Handle)[i] );
}
delete [] (HANDLE*)m_Handle;
#else
for( unsigned int i = 0; i < m_NumEvents; i++ )
{
if( m_bNamed )
{
sem_t* & HandlePtr = ((sem_t**)m_Handle)[i];
char c_Str[32];
sprintf( c_Str, "cEvent%p", &HandlePtr );
// LOG("Closing event: %s", c_Str );
// LOG("Sem ptr: %p", HandlePtr );
if( sem_close( HandlePtr ) != 0 )
{
LOG("ERROR: Could not close cEvent. (%i)", errno);
}
}
else
{
sem_destroy( ((sem_t**)m_Handle)[i] );
delete ((sem_t**)m_Handle)[i];
}
}
delete [] (sem_t**)m_Handle; m_Handle = 0;
#endif
}
void cEvent::Wait()
{
#ifdef _WIN32
WaitForMultipleObjects( m_NumEvents, (HANDLE*)m_Handle, true, INFINITE );
#else
for(unsigned int i = 0; i < m_NumEvents; i++)
{
if( sem_wait( ((sem_t**)m_Handle)[i] ) != 0 )
{
LOG("ERROR: Could not wait for cEvent. (%i)", errno);
}
}
#endif
}
void cEvent::Set(unsigned int a_EventNum /* = 0 */)
{
#ifdef _WIN32
SetEvent( ((HANDLE*)m_Handle)[a_EventNum] );
#else
if( sem_post( ((sem_t**)m_Handle)[a_EventNum] ) != 0 )
{
LOG("ERROR: Could not set cEvent. (%i)", errno);
}
#endif
}

18
source/cEvent.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
class cEvent
{
public:
cEvent( unsigned int a_NumEvents = 1 );
~cEvent();
void Wait();
void Set(unsigned int a_EventNum = 0);
private:
unsigned int m_NumEvents;
void* m_Handle; // HANDLE[] pointer
#ifndef _WIN32
bool m_bNamed;
#endif
};

372
source/cFurnaceEntity.cpp Normal file
View File

@ -0,0 +1,372 @@
#include "cFurnaceEntity.h"
#include "BlockID.h"
#include "cItem.h"
#include "cFurnaceWindow.h"
#include "cPlayer.h"
#include "cWorld.h"
#include "cChunk.h"
#include "cClientHandle.h"
#include "cFurnaceRecipe.h"
#include "cServer.h"
#include "cPickup.h"
#include "cRoot.h"
#include "packets/cPacket_InventoryProgressBar.h"
#include "cMCLogger.h"
#include <json/json.h>
cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z)
: cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z )
, m_Items( new cItem[3] )
, m_CookingItem( 0 )
, m_CookTime( 0 )
, m_TimeCooked( 0 )
, m_BurnTime( 0 )
, m_TimeBurned( 0 )
{
}
cFurnaceEntity::~cFurnaceEntity()
{
// Tell window its owner is destroyed
if( GetWindow() )
{
GetWindow()->OwnerDestroyed();
}
// Clean up items
if( m_Items )
{
delete [] m_Items;
}
}
void cFurnaceEntity::Destroy()
{
// Drop items
for( int i = 0; i < 3; i++)
{
if( !m_Items[i].IsEmpty() )
{
cPickup* Pickup = new cPickup( m_PosX*32 + 16, m_PosY*32 + 16, m_PosZ*32 + 16, m_Items[i], 0, 1.f, 0 );
Pickup->Initialize();
m_Items[i].Empty();
}
}
// Remove from tick list
cWorld* World = cRoot::Get()->GetWorld();
cChunk* Chunk = World->GetChunkOfBlock( m_PosX, m_PosY, m_PosZ );
Chunk->RemoveTickBlockEntity( this );
}
void cFurnaceEntity::UsedBy( cPlayer & a_Player )
{
LOG("Used a furnace");
if( !GetWindow() )
{
cWindow* Window = new cFurnaceWindow( this );
Window->SetSlots( m_Items, 3 );
Window->SetWindowTitle("UberFurnace");
OpenWindow( Window );
}
if( GetWindow() )
{
if( a_Player.GetWindow() != GetWindow() )
{
a_Player.OpenWindow( GetWindow() );
GetWindow()->SendWholeWindow( a_Player.GetClientHandle() );
}
}
}
bool cFurnaceEntity::Tick( float a_Dt )
{
//LOG("Time left: %0.1f Time burned: %0.1f Burn time: %0.1f", m_CookTime - m_TimeCooked, m_TimeBurned, m_BurnTime );
if( m_CookingItem && ( (m_TimeBurned < m_BurnTime) || (m_TimeCooked + a_Dt >= m_CookTime) ) )
{
if( m_CookingItem->Equals( m_Items[2] ) || m_Items[2].IsEmpty() )
{
m_TimeCooked += a_Dt;
if( m_TimeCooked >= m_CookTime )
{
m_Items[0].m_ItemCount--;
if( m_Items[0].IsEmpty() ) m_Items[0].Empty();
m_Items[2].m_ItemHealth = m_CookingItem->m_ItemHealth;
m_Items[2].m_ItemID = m_CookingItem->m_ItemID;
m_Items[2].m_ItemCount += m_CookingItem->m_ItemCount;
delete m_CookingItem;
m_CookingItem = 0;
cWindow* Window = GetWindow();
if( Window )
{
const std::list< cPlayer* > & OpenedBy = Window->GetOpenedBy();
for( std::list< cPlayer* >::const_iterator itr = OpenedBy.begin(); itr != OpenedBy.end(); ++itr )
{
Window->SendWholeWindow( (*itr)->GetClientHandle() );
}
}
m_TimeCooked = 0.f;
StartCooking();
}
cWindow* Window = GetWindow();
if( Window )
{
const std::list< cPlayer* > & OpenedBy = Window->GetOpenedBy();
for( std::list< cPlayer* >::const_iterator itr = OpenedBy.begin(); itr != OpenedBy.end(); ++itr )
{
cClientHandle* Client = (*itr)->GetClientHandle();
cPacket_InventoryProgressBar Progress;
Progress.m_ProgressBar = 0;
Progress.m_WindowID = (char)Window->GetWindowID();
Progress.m_Value = (short)( m_TimeCooked * (180.f/m_CookTime) );
if( Progress.m_Value > 180 ) Progress.m_Value = 180;
if( Progress.m_Value < 0 ) Progress.m_Value = 0;
Client->Send( Progress );
}
}
}
}
m_TimeBurned += a_Dt;
cWindow* Window = GetWindow();
if( m_TimeBurned >= m_BurnTime )
{
m_TimeBurned -= m_BurnTime;
m_BurnTime = 0;
if( StartCooking() && Window )
{
const std::list< cPlayer* > & OpenedBy = Window->GetOpenedBy();
for( std::list< cPlayer* >::const_iterator itr = OpenedBy.begin(); itr != OpenedBy.end(); ++itr )
{
Window->SendWholeWindow( (*itr)->GetClientHandle() );
}
}
}
if( Window )
{
const std::list< cPlayer* > & OpenedBy = Window->GetOpenedBy();
for( std::list< cPlayer* >::const_iterator itr = OpenedBy.begin(); itr != OpenedBy.end(); ++itr )
{
cClientHandle* Client = (*itr)->GetClientHandle();
cPacket_InventoryProgressBar Progress;
Progress.m_WindowID = (char)Window->GetWindowID();
Progress.m_ProgressBar = 1;
if( m_BurnTime > 0.f ) Progress.m_Value = (short)( m_TimeBurned * (150.f/m_BurnTime) );
else Progress.m_Value = 0;
if( Progress.m_Value > 150 ) Progress.m_Value = 150;
if( Progress.m_Value < 0 ) Progress.m_Value = 0;
Client->Send( Progress );
}
}
return ((m_CookingItem != 0) || (m_TimeBurned < m_BurnTime)) && m_BurnTime > 0.f; // Keep on ticking, if there's more to cook, or if it's cooking
}
bool cFurnaceEntity::StartCooking()
{
cFurnaceRecipe* FR = cRoot::Get()->GetFurnaceRecipe();
float BurnTime = FR->GetBurnTime( m_Items[1] );
if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material
{
const cFurnaceRecipe::Recipe* R = FR->GetRecipeFrom( m_Items[0] );
if( R ) // cook able ingredient
{
if( m_Items[2].Equals( *R->Out ) || m_Items[2].IsEmpty() )
{
// good to go
if( m_TimeBurned >= m_BurnTime ) // burn new material
{
m_Items[1].m_ItemCount--;
if( m_Items[1].m_ItemCount <= 0 ) m_Items[1].Empty();
m_TimeBurned = 0;
m_BurnTime = BurnTime;
}
if( !m_CookingItem ) // Only cook new item if not already cooking
{
m_CookingItem = new cItem( *R->Out ); // Resulting item
m_TimeCooked = 0.f;
m_CookTime = R->CookTime;
}
cWorld* World = cRoot::Get()->GetWorld();
cChunk* Chunk = World->GetChunkOfBlock( m_PosX, m_PosY, m_PosZ );
Chunk->AddTickBlockEntity( this );
return true;
}
}
}
return false;
}
void cFurnaceEntity::ResetCookTimer()
{
if( m_CookingItem )
{
delete m_CookingItem;
m_CookingItem = 0;
}
m_TimeCooked = 0.f;
m_CookTime = 0.f;
}
void cFurnaceEntity::WriteToFile(FILE* a_File)
{
fwrite( &m_BlockType, sizeof( ENUM_BLOCK_ID ), 1, a_File );
fwrite( &m_PosX, sizeof( int ), 1, a_File );
fwrite( &m_PosY, sizeof( int ), 1, a_File );
fwrite( &m_PosZ, sizeof( int ), 1, a_File );
unsigned int NumSlots = 3;
fwrite( &NumSlots, sizeof(unsigned int), 1, a_File );
for(unsigned int i = 0; i < NumSlots; i++)
{
cItem* Item = &m_Items[i];
if( Item )
{
fwrite( &Item->m_ItemID, sizeof(Item->m_ItemID), 1, a_File );
fwrite( &Item->m_ItemCount, sizeof(Item->m_ItemCount), 1, a_File );
fwrite( &Item->m_ItemHealth, sizeof(Item->m_ItemHealth), 1, a_File );
}
}
cItem Item;
if( m_CookingItem ) Item = *m_CookingItem;
fwrite( &Item.m_ItemID, sizeof(Item.m_ItemID), 1, a_File );
fwrite( &Item.m_ItemCount, sizeof(Item.m_ItemCount), 1, a_File );
fwrite( &Item.m_ItemHealth, sizeof(Item.m_ItemHealth), 1, a_File );
fwrite( &m_CookTime, sizeof(float), 1, a_File );
fwrite( &m_TimeCooked, sizeof(float), 1, a_File );
fwrite( &m_BurnTime, sizeof(float), 1, a_File );
fwrite( &m_TimeBurned, sizeof(float), 1, a_File );
}
bool cFurnaceEntity::LoadFromFile(FILE* a_File)
{
if( fread( &m_PosX, sizeof(int), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &m_PosY, sizeof(int), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &m_PosZ, sizeof(int), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
unsigned int NumSlots = 0;
if( fread( &NumSlots, sizeof(unsigned int), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
m_Items = new cItem[ NumSlots ];
for(unsigned int i = 0; i < NumSlots; i++)
{
cItem & Item = m_Items[ i ];
if( fread( &Item.m_ItemID, sizeof(Item.m_ItemID), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &Item.m_ItemCount, sizeof(Item.m_ItemCount), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &Item.m_ItemHealth, sizeof(Item.m_ItemHealth), 1, a_File)!= 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
}
cItem Item;
if( fread( &Item.m_ItemID, sizeof(Item.m_ItemID), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &Item.m_ItemCount, sizeof(Item.m_ItemCount), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &Item.m_ItemHealth, sizeof(Item.m_ItemHealth), 1, a_File)!= 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( !Item.IsEmpty() ) m_CookingItem = new cItem( Item );
if( fread( &m_CookTime, sizeof(float), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &m_TimeCooked, sizeof(float), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &m_BurnTime, sizeof(float), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
if( fread( &m_TimeBurned, sizeof(float), 1, a_File) != 1 ) { LOGERROR("ERROR READING FURNACE FROM FILE"); return false; }
return true;
}
bool cFurnaceEntity::LoadFromJson( const Json::Value& a_Value )
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
Json::Value AllSlots = a_Value.get("Slots", 0);
int SlotIdx = 0;
for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr )
{
Json::Value & Slot = *itr;
cItem & Item = m_Items[ SlotIdx ];
Item.m_ItemID = (ENUM_ITEM_ID)Slot.get("ID", -1 ).asInt();
if( Item.m_ItemID > 0 )
{
Item.m_ItemCount = (char)Slot.get("Count", -1 ).asInt();
Item.m_ItemHealth = (short)Slot.get("Health", -1 ).asInt();
}
SlotIdx++;
}
// Get currently cooking item
Json::Value JsonItem = a_Value.get("Cooking", Json::nullValue );
if( !JsonItem.empty() )
{
cItem Item;
Item.m_ItemID = (ENUM_ITEM_ID)JsonItem.get("ID", -1).asInt();
if( Item.m_ItemID > 0 )
{
Item.m_ItemCount = (char)JsonItem.get("Count", -1).asInt();
Item.m_ItemHealth = (short)JsonItem.get("Health", -1).asInt();
}
if( !Item.IsEmpty() )
{
m_CookingItem = new cItem( Item );
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunkOfBlock( m_PosX, m_PosY, m_PosZ );
Chunk->AddTickBlockEntity( this );
}
}
m_CookTime = (float)a_Value.get("CookTime", 0).asDouble();
m_TimeCooked = (float)a_Value.get("TimeCooked", 0).asDouble();
m_BurnTime = (float)a_Value.get("BurnTime", 0).asDouble();
m_TimeBurned = (float)a_Value.get("TimeBurned", 0).asDouble();
return true;
}
void cFurnaceEntity::SaveToJson( Json::Value& a_Value )
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
Json::Value AllSlots;
for(unsigned int i = 0; i < 3; i++)
{
Json::Value Slot;
cItem & Item = m_Items[ i ];
Slot["ID"] = Item.m_ItemID;
if( Item.m_ItemID > 0 )
{
Slot["Count"] = Item.m_ItemCount;
Slot["Health"] = Item.m_ItemHealth;
}
AllSlots.append( Slot );
}
a_Value["Slots"] = AllSlots;
// Currently cooking item
if( m_CookingItem )
{
Json::Value JsonItem;
JsonItem["ID"] = m_CookingItem->m_ItemID;
if( m_CookingItem->m_ItemID > 0 )
{
JsonItem["Count"] = m_CookingItem->m_ItemCount;
JsonItem["Health"] = m_CookingItem->m_ItemHealth;
}
a_Value["Cooking"] = JsonItem;
}
a_Value["CookTime"] = m_CookTime;
a_Value["TimeCooked"] = m_TimeCooked;
a_Value["BurnTime"] = m_BurnTime;
a_Value["TimeBurned"] = m_TimeBurned;
}

43
source/cFurnaceEntity.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "cBlockEntity.h"
#include "cWindowOwner.h"
#include "FileDefine.h"
namespace Json
{
class Value;
}
class cClientHandle;
class cServer;
class cItem;
class cNBTData;
class cFurnaceEntity : public cBlockEntity, public cWindowOwner
{
public:
cFurnaceEntity(int a_X, int a_Y, int a_Z);
virtual ~cFurnaceEntity();
virtual void Destroy();
void WriteToFile(FILE* a_File);
bool LoadFromFile(FILE* a_File);
bool LoadFromJson( const Json::Value& a_Value );
void SaveToJson( Json::Value& a_Value );
bool Tick( float a_Dt );
virtual void UsedBy( cPlayer & a_Player );
bool StartCooking();
void ResetCookTimer();
private:
cItem* m_Items;
cItem* m_CookingItem;
float m_CookTime;
float m_TimeCooked;
float m_BurnTime;
float m_TimeBurned;
};

214
source/cFurnaceRecipe.cpp Normal file
View File

@ -0,0 +1,214 @@
#include "cFurnaceRecipe.h"
#include "cItem.h"
#include "cMCLogger.h"
#include <fstream>
#include <sstream>
typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
typedef std::list< cFurnaceRecipe::Fuel > FuelList;
struct cFurnaceRecipe::sFurnaceRecipeState
{
RecipeList Recipes;
FuelList Fuel;
};
cFurnaceRecipe::cFurnaceRecipe()
: m_pState( new sFurnaceRecipeState )
{
ReloadRecipes();
}
cFurnaceRecipe::~cFurnaceRecipe()
{
ClearRecipes();
delete m_pState;
}
void cFurnaceRecipe::ReloadRecipes()
{
ClearRecipes();
LOG("--Loading furnace recipes--");
std::ifstream f;
char a_File[] = "furnace.txt";
f.open(a_File, std::ios::in);
std::string input;
if( !f.good() )
{
f.close();
LOG("Could not open file for recipes: %s", a_File);
return;
}
bool bSyntaxError = false;
while( f.good() )
{
char c;
//////////////////////////////////////////////////////////////////////////
// comments
f >> c;
f.unget();
if( c == '#' )
{
while( f.good() && c != '\n' )
{
f.get( c );
}
continue;
}
//////////////////////////////////////////////////////////////////////////
// Line breaks
f.get( c );
while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); }
if( f.eof() ) break;
f.unget();
//////////////////////////////////////////////////////////////////////////
// Check for fuel
f >> c;
if( c == '!' ) // It's fuel :)
{
// Read item
int IItemID = 0, IItemCount = 0, IItemHealth = 0;
f >> IItemID;
f >> c; if( c != ':' ) { bSyntaxError = true; break; }
f >> IItemCount;
// Optional health
f >> c;
if( c != ':' )
f.unget();
else
{
f >> IItemHealth;
}
// Burn time
float BurnTime;
f >> c; if( c != '=' ) { bSyntaxError = true; break; }
f >> BurnTime;
// Add to fuel list
Fuel F;
F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth );
F.BurnTime = BurnTime;
m_pState->Fuel.push_back( F );
continue;
}
f.unget();
//////////////////////////////////////////////////////////////////////////
// Read items
int IItemID = 0, IItemCount = 0, IItemHealth = 0;
f >> IItemID;
f >> c; if( c != ':' ) { bSyntaxError = true; break; }
f >> IItemCount;
// Optional health
f >> c;
if( c != ':' )
f.unget();
else
{
f >> IItemHealth;
}
float CookTime;
f >> c; if( c != '@' ) { bSyntaxError = true; break; }
f >> CookTime;
int OItemID = 0, OItemCount = 0, OItemHealth = 0;
f >> c; if( c != '=' ) { bSyntaxError = true; break; }
f >> OItemID;
f >> c; if( c != ':' ) { bSyntaxError = true; break; }
f >> OItemCount;
// Optional health
f >> c;
if( c != ':' )
f.unget();
else
{
f >> OItemHealth;
}
// Add to recipe list
Recipe R;
R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth );
R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth );
R.CookTime = CookTime;
m_pState->Recipes.push_back( R );
}
if( bSyntaxError )
{
LOGERROR("ERROR: FurnaceRecipe, syntax error" );
}
LOG("Got %i furnace recipes, and %i fuels.", m_pState->Recipes.size(), m_pState->Fuel.size() );
LOG("--Done loading furnace recipes--");
}
void cFurnaceRecipe::ClearRecipes()
{
for( RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr )
{
Recipe R = *itr;
delete R.In;
delete R.Out;
}
m_pState->Recipes.clear();
for( FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr )
{
Fuel F = *itr;
delete F.In;
}
m_pState->Fuel.clear();
}
const cFurnaceRecipe::Recipe* cFurnaceRecipe::GetRecipeFrom( const cItem & a_Ingredient ) const
{
const Recipe* BestRecipe = 0;
for( RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr )
{
const Recipe & R = *itr;
if( (R.In->m_ItemID == a_Ingredient.m_ItemID) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount ) )
{
if( BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount) )
{
continue;
}
else
{
BestRecipe = &R;
}
}
}
return BestRecipe;
}
float cFurnaceRecipe::GetBurnTime( const cItem & a_Fuel ) const
{
float BestFuel = 0.f;
for( FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr )
{
const Fuel & F = *itr;
if( (F.In->m_ItemID == a_Fuel.m_ItemID) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount ) )
{
if( BestFuel > 0.f && (BestFuel > F.BurnTime ) )
{
continue;
}
else
{
BestFuel = F.BurnTime;
}
}
}
return BestFuel;
}

34
source/cFurnaceRecipe.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <list>
class cItem;
class cFurnaceRecipe
{
public:
cFurnaceRecipe();
~cFurnaceRecipe();
void ReloadRecipes();
struct Fuel
{
cItem* In;
float BurnTime;
};
struct Recipe
{
cItem* In;
cItem* Out;
float CookTime;
};
const Recipe* GetRecipeFrom( const cItem & a_Ingredient ) const;
float GetBurnTime( const cItem & a_Fuel ) const;
private:
void ClearRecipes();
struct sFurnaceRecipeState;
sFurnaceRecipeState* m_pState;
};

42
source/cFurnaceWindow.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "cFurnaceWindow.h"
#include "cItem.h"
#include "cFurnaceEntity.h"
#include "cPlayer.h"
#include "packets/cPacket_WindowClick.h"
#include "cMCLogger.h"
cFurnaceWindow::cFurnaceWindow( cFurnaceEntity* a_Owner )
: cWindow( a_Owner, true )
, m_Furnace( a_Owner )
{
SetWindowID( 1 );
SetWindowType( 2 ); // Furnace
}
void cFurnaceWindow::Clicked( cPacket_WindowClick* a_ClickPacket, cPlayer & a_Player )
{
cItem Fuel = *GetSlot( 0 );
cWindow::Clicked( a_ClickPacket, a_Player );
if( m_Furnace )
{
if( a_ClickPacket->m_SlotNum >= 0 && a_ClickPacket->m_SlotNum <= 2 ) // them important slots
{
if( Fuel.m_ItemID != GetSlot( 0 )->m_ItemID )
m_Furnace->ResetCookTimer();
if( m_Furnace->StartCooking() )
{
SendWholeWindow( a_Player.GetClientHandle() );
}
}
}
}
void cFurnaceWindow::Close( cPlayer & a_Player )
{
m_Furnace = 0;
cWindow::Close( a_Player );
}

16
source/cFurnaceWindow.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "cWindow.h"
class cFurnaceEntity;
class cWindowOwner;
class cFurnaceWindow : public cWindow
{
public:
cFurnaceWindow( cFurnaceEntity* a_Owner );
virtual void Clicked( cPacket_WindowClick* a_ClickPacket, cPlayer & a_Player );
virtual void Close( cPlayer & a_Player );
private:
cFurnaceEntity* m_Furnace;
};

10
source/cGenSettings.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "cGenSettings.h"
float cGenSettings::HeightFreq1 = 0.1f;
float cGenSettings::HeightFreq2 = 1.f;
float cGenSettings::HeightFreq3 = 2.f;
float cGenSettings::HeightAmp1 = 1.f;
float cGenSettings::HeightAmp2 = 0.5f;
float cGenSettings::HeightAmp3 = 0.5f;

9
source/cGenSettings.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
class cGenSettings
{
public:
static float HeightFreq1, HeightAmp1;
static float HeightFreq2, HeightAmp2;
static float HeightFreq3, HeightAmp3;
};

34
source/cGroup.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "cGroup.h"
void cGroup::AddCommand( std::string a_Command )
{
m_Commands[ a_Command ] = true;
}
void cGroup::AddPermission( std::string a_Permission )
{
m_Permissions[ a_Permission ] = true;
}
bool cGroup::HasCommand( std::string a_Command )
{
if( m_Commands.find("*") != m_Commands.end() ) return true;
CommandMap::iterator itr = m_Commands.find( a_Command );
if( itr != m_Commands.end() )
{
if( itr->second ) return true;
}
for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr )
{
if( (*itr)->HasCommand( a_Command ) ) return true;
}
return false;
}
void cGroup::InheritFrom( cGroup* a_Group )
{
m_Inherits.remove( a_Group );
m_Inherits.push_back( a_Group );
}

39
source/cGroup.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <map>
#include <list>
class cGroup //tolua_export
{ //tolua_export
public: //tolua_export
cGroup() {}
~cGroup() {}
void SetName( std::string a_Name ) { m_Name = a_Name; } //tolua_export
const std::string & GetName() const { return m_Name; } //tolua_export
void SetColor( std::string a_Color ) { m_Color = a_Color; } //tolua_export
void AddCommand( std::string a_Command ); //tolua_export
void AddPermission( std::string a_Permission ); //tolua_export
void InheritFrom( cGroup* a_Group ); //tolua_export
bool HasCommand( std::string a_Command ); //tolua_export
typedef std::map< std::string, bool > PermissionMap;
const PermissionMap & GetPermissions() const { return m_Permissions; }
typedef std::map< std::string, bool > CommandMap;
const CommandMap & GetCommands() const { return m_Commands; }
std::string GetColor() const { return m_Color; } //tolua_export
typedef std::list< cGroup* > GroupList;
const GroupList & GetInherits() const { return m_Inherits; }
private:
std::string m_Name;
std::string m_Color;
PermissionMap m_Permissions;
CommandMap m_Commands;
GroupList m_Inherits;
};//tolua_export

104
source/cGroupManager.cpp Normal file
View File

@ -0,0 +1,104 @@
#include "cGroupManager.h"
#include "cGroup.h"
#include "../iniFile/iniFile.h"
#include "cChatColor.h"
#include "cMCLogger.h"
#include "cRoot.h"
extern std::vector< std::string > StringSplit( std::string str, std::string delim);
typedef std::map< std::string, cGroup* > GroupMap;
struct cGroupManager::sGroupManagerState
{
GroupMap Groups;
};
cGroupManager* cGroupManager::GetGroupManager()
{
LOGWARN("WARNING: Using deprecated function cGroupManager::GetGroupManager() use cRoot::Get()->GetGroupManager() instead!");
return cRoot::Get()->GetGroupManager();
}
cGroupManager::~cGroupManager()
{
for( GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr )
{
delete itr->second;
}
m_pState->Groups.clear();
delete m_pState;
}
cGroupManager::cGroupManager()
: m_pState( new sGroupManagerState )
{
LOG("-- Loading Groups --");
cIniFile IniFile("groups.ini");
if( IniFile.ReadFile() )
{
unsigned int NumKeys = IniFile.GetNumKeys();
for( unsigned int i = 0; i < NumKeys; i++ )
{
std::string KeyName = IniFile.GetKeyName( i );
cGroup* Group = GetGroup( KeyName.c_str() );
LOG("Loading group: %s", KeyName.c_str() );
Group->SetName( KeyName );
char Color = IniFile.GetValue( KeyName, "Color", "-" )[0];
if( Color != '-' )
Group->SetColor( cChatColor::MakeColor(Color) );
else
Group->SetColor( cChatColor::White );
std::string Commands = IniFile.GetValue( KeyName, "Commands", "" );
if( Commands.size() > 0 )
{
std::vector< std::string > Split = StringSplit( Commands, "," );
for( unsigned int i = 0; i < Split.size(); i++)
{
Group->AddCommand( Split[i] );
//LOG("%s", Split[i].c_str() );
}
}
std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" );
if( Permissions.size() > 0 )
{
std::vector< std::string > Split = StringSplit( Permissions, "," );
for( unsigned int i = 0; i < Split.size(); i++)
{
Group->AddPermission( Split[i] );
LOGINFO("Permission: %s", Split[i].c_str() );
}
}
std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" );
if( Groups.size() > 0 )
{
std::vector< std::string > Split = StringSplit( Groups, "," );
for( unsigned int i = 0; i < Split.size(); i++)
{
Group->InheritFrom( GetGroup( Split[i].c_str() ) );
}
}
}
}
LOG("-- Done Loading Groups --");
}
cGroup* cGroupManager::GetGroup( const char* a_Name )
{
GroupMap::iterator itr = m_pState->Groups.find( a_Name );
if( itr != m_pState->Groups.end() )
{
return itr->second;
}
cGroup* Group = new cGroup();
m_pState->Groups[a_Name] = Group;
return Group;
}

17
source/cGroupManager.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
class cGroup;
class cGroupManager
{
public:
static cGroupManager * GetGroupManager(); //tolua_export
cGroup* GetGroup( const char* a_Name );
private:
friend class cRoot;
cGroupManager();
~cGroupManager();
struct sGroupManagerState;
sGroupManagerState* m_pState;
};

119
source/cHeartBeat.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "cHeartBeat.h"
#include "cMCLogger.h"
#include "md5/md5.h"
#include <stdio.h>
#include "cRoot.h"
#include "cServer.h"
#include "cSleep.h"
cHeartBeat::cHeartBeat()
{
m_State = 0;
Authenticate();
}
cHeartBeat::~cHeartBeat()
{
}
void cHeartBeat::ReceivedData( char a_Data[256], int a_Size )
{
if( a_Size < 0 ) // Disconnected
return;
char MySalt[] = "1234567890";
if( a_Size == 0 )
{
Authenticate();
return;
}
bool bLoop = false;
do
{
switch (m_State)
{
case 1:
{
m_ServerID = std::string( a_Data, a_Size );
LOGINFO("Got server ID %s", m_ServerID.c_str() );
std::string Hash = md5( m_ServerID + std::string( MySalt ) );
CloseSocket();
if( Connect( "mc-server.org", 80 ) )
{
SendMessage( (std::string("GET http://master.mc-server.org/?hash=") + Hash + std::string("&server=") + m_ServerID + "\n").c_str() );
m_State = 2;
}
}
break;
case 2:
{
std::string ReturnedString = std::string( a_Data, a_Size );
if( ReturnedString.compare("VALIDATED") == 0 )
{
LOGINFO("Successfully validated server on master server list");
}
else
{
LOGINFO("Could not validate server! Will try again later.");
cSleep::MilliSleep( 10*1000 );
Authenticate();
return;
}
m_State = 3;
} // Don't break, but fall through and update server info
case 3:
{
cSleep::MilliSleep( 10*1000 );
SendUpdate();
m_State = 4;
}
break;
case 4:
{
if( a_Data[0] == '0' )
{
LOGINFO("Successfully updated server info!");
cSleep::MilliSleep( 10*1000 );
SendUpdate();
}
else
{
LOGINFO("Failed to update server info, reauthenticating");
Authenticate();
}
}
default:
break;
};
} while( bLoop );
}
void cHeartBeat::SendUpdate()
{
CloseSocket();
if( Connect( "mc-server.org", 80 ) )
{
int Port = cRoot::Get()->GetServer()->GetPort();
char c_Port[16];
sprintf_s( c_Port, 16, "%i", Port );
std::string sPort = std::string( c_Port );
std::string sChecksum = md5( m_ServerID + sPort );
SendMessage( (std::string("GET http://master.mc-server.org/?update=") + m_ServerID + std::string("&checksum=") + sChecksum + std::string("&port=") + sPort + "\n").c_str() );
}
}
void cHeartBeat::Authenticate()
{
CloseSocket();
if( Connect( "mc-server.org", 80 ) )
{
m_State = 1;
int RetVal = SendMessage( "GET http://master.mc-server.org/\r\n\r\n");
LOGINFO("Returned %i", RetVal );
}
}

21
source/cHeartBeat.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "cTCPLink.h"
#include <string>
class cHeartBeat : public cTCPLink
{
public:
cHeartBeat();
~cHeartBeat();
private:
virtual void ReceivedData( char a_Data[256], int a_Size );
void Authenticate();
int m_State;
void SendUpdate();
std::string m_ServerID;
};

364
source/cInventory.cpp Normal file
View File

@ -0,0 +1,364 @@
#include "cInventory.h"
#include <string> //memset
#include "cPlayer.h"
#include "cClientHandle.h"
#include "cMCLogger.h"
#include "cWindow.h"
#include "cItem.h"
#include "cRecipeChecker.h"
#include "cRoot.h"
#include "packets/cPacket_WindowClick.h"
#include "packets/cPacket_WholeInventory.h"
#include "packets/cPacket_InventorySlot.h"
cInventory::~cInventory()
{
delete [] m_Slots;
delete m_EquippedItem;
if( GetWindow() ) GetWindow()->Close( *m_Owner );
CloseWindow();
}
cInventory::cInventory(cPlayer* a_Owner)
{
m_Owner = a_Owner;
m_Slots = new cItem[c_NumSlots];
for(unsigned int i = 0; i < c_NumSlots; i++)
m_Slots[i].Empty();
m_CraftSlots = m_Slots + c_CraftOffset;
m_ArmorSlots = m_Slots + c_ArmorOffset;
m_MainSlots = m_Slots + c_MainOffset;
m_HotSlots = m_Slots + c_HotOffset;
m_EquippedItem = new cItem();
m_EquippedSlot = 0;
if( !GetWindow() )
{
cWindow* Window = new cWindow( this, false );
Window->SetSlots( m_Slots, c_NumSlots );
Window->SetWindowID( 0 );
OpenWindow( Window );
}
}
void cInventory::Clear()
{
for(unsigned int i = 0; i < c_NumSlots; i++)
m_Slots[i].Empty();
}
cItem* cInventory::GetSlotsForType( int a_Type )
{
switch( a_Type )
{
case -1:
return m_MainSlots;
case -2:
return m_CraftSlots;
case -3:
return m_ArmorSlots;
}
return 0;
}
int cInventory::GetSlotCountForType( int a_Type )
{
switch( a_Type )
{
case -1:
return 36;
case -2:
case -3:
return 4;
}
return 0;
}
void cInventory::Clicked( cPacket_WindowClick* a_ClickPacket )
{
bool bDontCook = false;
if( GetWindow() )
{
// Override for craft result slot
if( a_ClickPacket->m_SlotNum == (short)c_CraftOffset )
{
LOG("In craft slot: %i x %i !!", m_Slots[c_CraftOffset].m_ItemID, m_Slots[c_CraftOffset].m_ItemCount );
cItem* DraggingItem = GetWindow()->GetDraggingItem();
if( DraggingItem->IsEmpty() )
{
*DraggingItem = m_Slots[c_CraftOffset];
m_Slots[c_CraftOffset].Empty();
}
else if( DraggingItem->Equals( m_Slots[c_CraftOffset] ) )
{
if( DraggingItem->m_ItemCount + m_Slots[c_CraftOffset].m_ItemCount <= 64 )
{
DraggingItem->m_ItemCount += m_Slots[c_CraftOffset].m_ItemCount;
m_Slots[0].Empty();
}
else
{
bDontCook = true;
}
}
else
{
bDontCook = true;
}
LOG("Dragging Dish %i", DraggingItem->m_ItemCount );
}
else
{
GetWindow()->Clicked( a_ClickPacket, *m_Owner );
}
}
else
{
LOG("No Inventory window! WTF");
}
if( a_ClickPacket->m_SlotNum >= (short)c_CraftOffset && a_ClickPacket->m_SlotNum < (short)(c_CraftOffset+c_CraftSlots+1) )
{
cItem CookedItem;
if( a_ClickPacket->m_SlotNum == 0 && !bDontCook )
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( m_Slots+c_CraftOffset+1, 2, 2, true );
}
else
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( m_Slots+c_CraftOffset+1, 2, 2 );
}
m_Slots[c_CraftOffset] = CookedItem;
LOG("You cooked: %i x %i !!", m_Slots[c_CraftOffset].m_ItemID, m_Slots[c_CraftOffset].m_ItemCount );
SendWholeInventory( m_Owner->GetClientHandle() );
}
SendSlot( 0 );
}
bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ )
{
// Fill already present stacks
if( a_Mode < 2 )
{
for(int i = 0; i < a_Size; i++)
{
if( m_Slots[i + a_Offset].m_ItemID == a_Item.m_ItemID && m_Slots[i + a_Offset].m_ItemCount < 64 )
{
int NumFree = 64 - m_Slots[i + a_Offset].m_ItemCount;
if( NumFree >= a_Item.m_ItemCount )
{
//printf("1. Adding %i items ( free: %i )\n", a_Item.m_ItemCount, NumFree );
m_Slots[i + a_Offset].m_ItemCount += a_Item.m_ItemCount;
a_Item.m_ItemCount = 0;
a_bChangedSlots[i + a_Offset] = true;
break;
}
else
{
//printf("2. Adding %i items\n", NumFree );
m_Slots[i + a_Offset].m_ItemCount += (char)NumFree;
a_Item.m_ItemCount -= (char)NumFree;
a_bChangedSlots[i + a_Offset] = true;
}
}
}
}
if( a_Mode > 0 )
{
// If we got more left, find first empty slot
for(int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++)
{
if( m_Slots[i + a_Offset].m_ItemID == -1 )
{
m_Slots[i + a_Offset] = a_Item;
a_Item.m_ItemCount = 0;
a_bChangedSlots[i + a_Offset] = true;
}
}
}
return true;
}
bool cInventory::AddItem( cItem & a_Item )
{
cItem BackupSlots[c_NumSlots];
memcpy( BackupSlots, m_Slots, c_NumSlots * sizeof( cItem ) );
bool ChangedSlots[c_NumSlots];
memset( ChangedSlots, false, c_NumSlots * sizeof( bool ) );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 0 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 0 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 2 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 2 );
if( a_Item.m_ItemCount > 0 ) // Could not add all items
{
// retore backup
memcpy( m_Slots, BackupSlots, c_NumSlots * sizeof( cItem ) );
return false;
}
for(unsigned int i = 0; i < c_NumSlots; i++)
{
if( ChangedSlots[i] )
{
LOG("Item was added to %i ID:%i Count:%i", i, m_Slots[i].m_ItemID, m_Slots[i].m_ItemCount );
SendSlot(i);
}
}
return (a_Item.m_ItemCount == 0);
}
// TODO: Right now if you dont have enough items, the items you did have are removed, and the function returns false anyway
bool cInventory::RemoveItem( cItem & a_Item )
{
// First check equipped slot
if( m_EquippedSlot >= 0 && m_EquippedSlot < 9 )
{
if( m_HotSlots[m_EquippedSlot].m_ItemID == a_Item.m_ItemID )
{
cItem & Item = m_HotSlots[m_EquippedSlot];
if(Item.m_ItemCount > a_Item.m_ItemCount)
{
Item.m_ItemCount -= a_Item.m_ItemCount;
SendSlot( m_EquippedSlot + c_HotOffset );
return true;
}
else if(Item.m_ItemCount > 0 )
{
a_Item.m_ItemCount -= Item.m_ItemCount;
Item.Empty();
SendSlot( m_EquippedSlot + c_HotOffset );
}
}
}
// Then check other slotz
if( a_Item.m_ItemCount > 0 )
{
for(int i = 0; i < 36; i++)
{
cItem & Item = m_MainSlots[i];
if( Item.m_ItemID == a_Item.m_ItemID )
{
if(Item.m_ItemCount > a_Item.m_ItemCount)
{
Item.m_ItemCount -= a_Item.m_ItemCount;
SendSlot( i + c_MainOffset );
return true;
}
else if(Item.m_ItemCount > 0 )
{
a_Item.m_ItemCount -= Item.m_ItemCount;
Item.Empty();
SendSlot( i + c_MainOffset );
}
}
}
}
if( a_Item.m_ItemCount == 0 )
return true;
else
return false;
}
cItem* cInventory::GetSlot( int a_SlotNum )
{
if( a_SlotNum < 0 || a_SlotNum >= (short)c_NumSlots ) return 0;
return &m_Slots[a_SlotNum];
}
cItem* cInventory::GetFromHotBar( int a_SlotNum )
{
if( a_SlotNum < 0 || a_SlotNum >= 9 ) return 0;
return &m_HotSlots[a_SlotNum];
}
void cInventory::SetEquippedSlot( int a_SlotNum )
{
if( a_SlotNum < 0 || a_SlotNum >= 9 ) m_EquippedSlot = 0;
else m_EquippedSlot = (short)a_SlotNum;
}
cItem & cInventory::GetEquippedItem()
{
cItem* Item = GetFromHotBar( m_EquippedSlot );
if( Item )
{
*m_EquippedItem = *Item;
return *Item;
}
else
{
m_EquippedItem->Empty();
}
return *m_EquippedItem;
}
void cInventory::SendWholeInventory( cClientHandle* a_Client )
{
cPacket_WholeInventory Inventory( this );
a_Client->Send( Inventory );
}
void cInventory::SendSlot( int a_SlotNum )
{
cItem* Item = GetSlot( a_SlotNum );
if( Item )
{
cPacket_InventorySlot InventorySlot;
InventorySlot.m_ItemCount = Item->m_ItemCount;
InventorySlot.m_ItemID = (short)Item->m_ItemID;
InventorySlot.m_ItemUses = (char)Item->m_ItemHealth;
InventorySlot.m_SlotNum = (short)a_SlotNum;
InventorySlot.m_WindowID = 0; // Inventory window ID
m_Owner->GetClientHandle()->Send( InventorySlot );
}
}
void cInventory::DrawInventory()
{
printf("%i %i %i %i\n", m_ArmorSlots[0].m_ItemCount, m_ArmorSlots[1].m_ItemCount, m_CraftSlots[0].m_ItemCount, m_CraftSlots[1].m_ItemCount );
printf("%i %i %i %i\n", m_ArmorSlots[2].m_ItemCount, m_ArmorSlots[3].m_ItemCount, m_CraftSlots[2].m_ItemCount, m_CraftSlots[3].m_ItemCount );
for(int y = 0; y < 4; y++)
{
for(int x = 0; x < 9; x++)
{
printf("%i ", m_MainSlots[x + y*9].m_ItemCount );
}
printf("\n");
}
}
void cInventory::WriteToFile(FILE* a_File)
{
for(unsigned int i = 0; i < c_NumSlots; i++)
{
cItem & Item = m_Slots[i];
fwrite( &Item.m_ItemID, sizeof(Item.m_ItemID), 1, a_File );
fwrite( &Item.m_ItemCount, sizeof(Item.m_ItemCount), 1, a_File );
fwrite( &Item.m_ItemHealth, sizeof(Item.m_ItemHealth), 1, a_File );
}
}
bool cInventory::LoadFromFile(FILE* a_File)
{
for(unsigned int i = 0; i < c_NumSlots; i++)
{
cItem & Item = m_Slots[i];
if( fread( &Item.m_ItemID, sizeof(Item.m_ItemID), 1, a_File) != 1 ) { LOGERROR("ERROR READING INVENTORY FROM FILE"); return false; }
if( fread( &Item.m_ItemCount, sizeof(Item.m_ItemCount), 1, a_File) != 1 ) { LOGERROR("ERROR READING INVENTORY FROM FILE"); return false; }
if( fread( &Item.m_ItemHealth, sizeof(Item.m_ItemHealth), 1, a_File)!= 1 ) { LOGERROR("ERROR READING INVENTORY FROM FILE"); return false; }
}
return true;
}

67
source/cInventory.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include "MemoryLeak.h"
#include "cWindowOwner.h"
#include "FileDefine.h"
class cItem;
class cClientHandle;
class cPlayer;
class cPacket_WindowClick;
class cPacket_EntityEquipment;
class cInventory //tolua_export
: public cWindowOwner
{ //tolua_export
public:
cInventory(cPlayer* a_Owner);
~cInventory();
void Clear(); //tolua_export
cItem* GetSlotsForType( int a_Type );
int GetSlotCountForType( int a_Type );
bool AddItem( cItem & a_Item ); //tolua_export
bool RemoveItem( cItem & a_Item ); //tolua_export
void DrawInventory();
void WriteToFile(FILE* a_File);
bool LoadFromFile(FILE* a_File);
void SendWholeInventory( cClientHandle* a_Client );
cItem* GetSlot( int a_SlotNum ); //tolua_export
cItem* GetSlots() { return m_Slots; }
cItem* GetFromHotBar( int a_SlotNum ); //tolua_export
cItem & GetEquippedItem(); //tolua_export
void SetEquippedSlot( int a_SlotNum ); //tolua_export
void Clicked( cPacket_WindowClick* a_ClickPacket );
void SendSlot( int a_SlotNum ); //tolua_export
static const unsigned int c_NumSlots = 45;
static const unsigned int c_MainSlots = 27;
static const unsigned int c_HotSlots = 9;
static const unsigned int c_CraftSlots = 4;
static const unsigned int c_ArmorSlots = 4;
static const unsigned int c_CraftOffset = 0;
static const unsigned int c_ArmorOffset = 5;
static const unsigned int c_MainOffset = 9;
static const unsigned int c_HotOffset = 36;
private:
bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 );
cItem* m_Slots;
cItem* m_MainSlots;
cItem* m_CraftSlots;
cItem* m_ArmorSlots;
cItem* m_HotSlots;
cItem* m_EquippedItem;
short m_EquippedSlot;
cPlayer* m_Owner;
}; //tolua_export

33
source/cItem.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include "Defines.h"
#include "BlockID.h"
class cItem //tolua_export
{ //tolua_export
public:
cItem( ENUM_ITEM_ID a_ItemID = E_ITEM_EMPTY, char a_ItemCount = 0, short a_ItemHealth = 0 ) //tolua_export
: m_ItemID ( a_ItemID )
, m_ItemCount ( a_ItemCount )
, m_ItemHealth ( a_ItemHealth )
{ //tolua_export
if(!isValidItem( m_ItemID ) ) m_ItemID = E_ITEM_EMPTY;
} //tolua_export
void Empty() //tolua_export
{ //tolua_export
m_ItemID = E_ITEM_EMPTY;
m_ItemCount = 0;
m_ItemHealth = 0;
} //tolua_export
bool IsEmpty() //tolua_export
{ //tolua_export
return (m_ItemID <= 0 || m_ItemCount <= 0);
} //tolua_export
bool Equals( cItem & a_Item ) //tolua_export
{ //tolua_export
return ( (m_ItemID == a_Item.m_ItemID) && (m_ItemHealth == a_Item.m_ItemHealth) );
} //tolua_export
ENUM_ITEM_ID m_ItemID; //tolua_export
char m_ItemCount; //tolua_export
short m_ItemHealth; //tolua_export
}; //tolua_export

43
source/cLadder.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
class cLadder //tolua_export
{ //tolua_export
public:
static char DirectionToMetaData( char a_Direction ) //tolua_export
{ //tolua_export
switch( a_Direction )
{
case 0x2:
return 0x2;
case 0x3:
return 0x3;
case 0x4:
return 0x4;
case 0x5:
return 0x5;
default:
break;
};
return 0x2;
} //tolua_export
static char MetaDataToDirection( char a_MetaData ) //tolua_export
{ //tolua_export
switch( a_MetaData )
{
case 0x2:
return 0x2;
case 0x3:
return 0x3;
case 0x4:
return 0x4;
case 0x5:
return 0x5;
default:
break;
};
return 0x2;
} //tolua_export
}; //tolua_export

136
source/cLog.cpp Normal file
View File

@ -0,0 +1,136 @@
#include "cLog.h"
#include <fstream>
#include <ctime>
#include <stdarg.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/stat.h> // for mkdir
#include <sys/types.h>
#define sprintf_s(buffer, buffer_size, stringbuffer, ...) (sprintf(buffer, stringbuffer, __VA_ARGS__))
#define vsnprintf_s(buffer, buffer_size, maxcount, stringbuffer, ...) (vsnprintf(buffer, maxcount, stringbuffer, __VA_ARGS__))
#endif
#include <string>
cLog* cLog::s_Log = NULL;
cLog::cLog( const char* a_FileName )
: m_File(NULL)
{
s_Log = this;
// create logs directory
#ifdef _WIN32
{
SECURITY_ATTRIBUTES Attrib;
Attrib.nLength = sizeof(SECURITY_ATTRIBUTES);
Attrib.lpSecurityDescriptor = NULL;
Attrib.bInheritHandle = false;
::CreateDirectory("logs", &Attrib);
}
#else
{
mkdir("logs", S_IRWXU | S_IRWXG | S_IRWXO);
}
#endif
OpenLog( (std::string("logs/") + std::string(a_FileName)).c_str() );
}
cLog::~cLog()
{
CloseLog();
s_Log = NULL;
}
cLog* cLog::GetInstance()
{
if(s_Log)
return s_Log;
new cLog("log.txt");
return s_Log;
}
void cLog::CloseLog()
{
if( m_File )
fclose (m_File);
m_File = 0;
}
void cLog::OpenLog( const char* a_FileName )
{
if(m_File) fclose (m_File);
#ifdef _WIN32
fopen_s( &m_File, a_FileName, "a+" );
#else
m_File = fopen(a_FileName, "a+" );
#endif
}
void cLog::ClearLog()
{
#ifdef _WIN32
if( fopen_s( &m_File, "log.txt", "w" ) == 0)
fclose (m_File);
#else
m_File = fopen("log.txt", "w" );
if( m_File )
fclose (m_File);
#endif
m_File = 0;
}
void cLog::Log(const char* a_Format, va_list argList )
{
char c_Buffer[1024];
if( argList != 0 )
{
vsnprintf_s(c_Buffer, 1024, 1024, a_Format, argList );
}
else
{
sprintf_s( c_Buffer, 1024, "%s", a_Format );
}
time_t rawtime;
time ( &rawtime );
#ifdef _WIN32
struct tm timeinfo;
localtime_s( &timeinfo, &rawtime );
#else
struct tm* timeinfo;
timeinfo = localtime( &rawtime );
#endif
char c_Buffer2[1024];
#ifdef _WIN32
sprintf_s(c_Buffer2, 1024, "[%02d:%02d:%02d] %s\n", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, c_Buffer);
#else
sprintf(c_Buffer2, "[%02d:%02d:%02d] %s\n", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, c_Buffer);
#endif
if(m_File){
fputs(c_Buffer2, m_File);
fflush(m_File);
}
printf("%s", c_Buffer2 );
}
void cLog::Log(const char* a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
Log( a_Format, argList );
va_end(argList);
}
void cLog::SimpleLog(const char* a_String)
{
Log("%s", a_String );
}

30
source/cLog.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include "MemoryLeak.h"
#include "FileDefine.h"
#ifndef _WIN32
#include <stdarg.h>
#endif
class cLog { // tolua_export
private:
FILE* m_File;
static cLog* s_Log;
#ifdef _WIN32
typedef char* va_list;
#endif
public:
cLog( const char* a_FileName );
~cLog();
void Log(const char* a_Format, va_list argList );
void Log(const char* a_Format, ...);
//tolua_begin
void SimpleLog(const char* a_String);
void OpenLog( const char* a_FileName );
void CloseLog();
void ClearLog();
static cLog* GetInstance();
};
//tolua_end

View File

@ -0,0 +1,106 @@
#include "cLuaCommandBinder.h"
#include "cMCLogger.h"
#include "cPlayer.h"
#include "cPlugin_Lua.h"
#include "tolua++.h"
extern std::vector<std::string> StringSplit(std::string str, std::string delim);
extern bool report_errors(lua_State* lua, int status);
cLuaCommandBinder::cLuaCommandBinder()
{
}
cLuaCommandBinder::~cLuaCommandBinder()
{
}
void cLuaCommandBinder::ClearBindings()
{
m_BoundCommands.clear();
}
void cLuaCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin )
{
for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); )
{
if( itr->second.Plugin == a_Plugin )
{
LOGINFO("Unbinding %s ", itr->first.c_str( ) );
luaL_unref( itr->second.LuaState, LUA_REGISTRYINDEX, itr->second.Reference ); // unreference
CommandMap::iterator eraseme = itr;
++itr;
m_BoundCommands.erase( eraseme );
continue;
}
++itr;
}
}
bool cLuaCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, lua_State * a_LuaState, int a_FunctionReference )
{
if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() )
{
LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() );
return false;
}
LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() );
m_BoundCommands[ a_Command ] = BoundFunction( a_Plugin, a_LuaState, a_FunctionReference, a_Permission );
return true;
}
bool cLuaCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player )
{
std::vector<std::string> Split = StringSplit( a_Command, " ");
CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] );
if( FoundCommand != m_BoundCommands.end() )
{
const BoundFunction & func = FoundCommand->second;
if( func.Permission.size() > 0 )
{
if( !a_Player->HasPermission( func.Permission.c_str() ) )
{
return false;
}
}
// For enabling 'self' in the function, it's kind of a hack I'm not sure this is the way to go
lua_pushvalue(func.LuaState, LUA_GLOBALSINDEX);
lua_pushstring(func.LuaState, "self");
tolua_pushusertype( func.LuaState, func.Plugin, "cPlugin" );
lua_rawset(func.LuaState, -3);
lua_pop(func.LuaState, 1);
LOGINFO("1. Stack size: %i", lua_gettop(func.LuaState) );
lua_rawgeti( func.LuaState, LUA_REGISTRYINDEX, func.Reference); // same as lua_getref()
// Push the split
LOGINFO("2. Stack size: %i", lua_gettop(func.LuaState) );
lua_createtable(func.LuaState, Split.size(), 0);
int newTable = lua_gettop(func.LuaState);
int index = 1;
std::vector<std::string>::const_iterator iter = Split.begin();
while(iter != Split.end()) {
tolua_pushstring( func.LuaState, (*iter).c_str() );
lua_rawseti(func.LuaState, newTable, index);
++iter;
++index;
}
LOGINFO("3. Stack size: %i", lua_gettop(func.LuaState) );
// Push player
tolua_pushusertype( func.LuaState, a_Player, "cPlayer" );
LOGINFO("Calling bound function! :D");
int s = lua_pcall(func.LuaState, 2, 1, 0);
if( report_errors( func.LuaState, s ) )
{
LOGINFO("error. Stack size: %i", lua_gettop(func.LuaState) );
return false;
}
bool RetVal = (tolua_toboolean(func.LuaState, -1, 0) > 0);
lua_pop(func.LuaState, 1); // Pop return value
LOGINFO("ok. Stack size: %i", lua_gettop(func.LuaState) );
return RetVal;
}
return false;
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include <string>
#include <map>
struct lua_State;
class cPlugin;
class cPlayer;
class cLuaCommandBinder
{
public:
cLuaCommandBinder();
~cLuaCommandBinder();
bool HandleCommand( const std::string & a_Command, cPlayer* a_Player );
bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, lua_State * a_LuaState, int a_FunctionReference );
void ClearBindings();
void RemoveBindingsForPlugin( cPlugin* a_Plugin );
private:
struct BoundFunction
{
BoundFunction() : Plugin( 0 ), LuaState( 0 ), Reference( 0 ) {}
BoundFunction( cPlugin* a_Plugin, lua_State * a_LuaState, int a_Reference, const std::string & a_Permission ) : Plugin( a_Plugin ), LuaState( a_LuaState ), Reference( a_Reference ), Permission( a_Permission ) {}
cPlugin* Plugin;
lua_State* LuaState;
int Reference;
std::string Permission;
};
typedef std::map< std::string, BoundFunction > CommandMap;
CommandMap m_BoundCommands;
};

155
source/cMCLogger.cpp Normal file
View File

@ -0,0 +1,155 @@
#include "cMCLogger.h"
#include "cLog.h"
#include "cCriticalSection.h"
#include <stdio.h>
#include <cstdarg>
#include <time.h>
#ifndef _WIN32
#define sprintf_s(buffer, buffer_size, stringbuffer, ...) (sprintf(buffer, stringbuffer, __VA_ARGS__))
#else
#include <Windows.h>
#endif
cMCLogger* cMCLogger::s_MCLogger = 0;
cMCLogger* cMCLogger::GetInstance()
{
return s_MCLogger;
}
cMCLogger::cMCLogger()
{
m_CriticalSection = new cCriticalSection();
char c_Buffer[128];
sprintf_s(c_Buffer, 128, "LOG_%d.txt", (int)time(0) );
m_Log = new cLog(c_Buffer);
m_Log->Log("--- Started Log ---");
s_MCLogger = this;
}
cMCLogger::cMCLogger( char* a_File )
{
m_CriticalSection = new cCriticalSection();
m_Log = new cLog( a_File );
}
cMCLogger::~cMCLogger()
{
m_Log->Log("--- Stopped Log ---");
delete m_Log;
delete m_CriticalSection;
if( this == s_MCLogger )
s_MCLogger = 0;
}
void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ )
{
switch( a_LogType )
{
case 0:
Log(a_Text, 0);
break;
case 1:
Info(a_Text, 0);
break;
case 2:
Warn(a_Text, 0);
break;
case 3:
Error(a_Text, 0);
break;
default:
Log(a_Text, 0);
break;
}
}
void cMCLogger::Log(const char* a_Format, va_list a_ArgList)
{
m_CriticalSection->Lock();
SetColor( 0x7 ); // 0x7 is default grey color
m_Log->Log( a_Format, a_ArgList );
m_CriticalSection->Unlock();
}
void cMCLogger::Info(const char* a_Format, va_list a_ArgList)
{
m_CriticalSection->Lock();
// for( int i = 0; i < 16; i++)
// {
// for( int j = 0; j < 16; j++ )
// {
// SetConsoleTextAttribute( hConsole, i | (j<<4) );
// printf("0x%x", (i|j<<4));
// }
// printf("\n");
// }
SetColor( 0xe ); // 0xe is yellow
m_Log->Log( a_Format, a_ArgList );
m_CriticalSection->Unlock();
}
void cMCLogger::Warn(const char* a_Format, va_list a_ArgList)
{
m_CriticalSection->Lock();
SetColor( 0xc ); // 0xc is red
m_Log->Log( a_Format, a_ArgList );
m_CriticalSection->Unlock();
}
void cMCLogger::Error(const char* a_Format, va_list a_ArgList)
{
m_CriticalSection->Lock();
SetColor( 0xc0 ); // 0xc0 is red bg and black text
m_Log->Log( a_Format, a_ArgList );
m_CriticalSection->Unlock();
}
void cMCLogger::SetColor( unsigned char a_Color )
{
#ifdef _WIN32
HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE );
SetConsoleTextAttribute( hConsole, a_Color );
#else
(void)a_Color;
#endif
}
//////////////////////////////////////////////////////////////////////////
// Global functions
void LOG(const char* a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
cMCLogger::GetInstance()->Log( a_Format, argList );
va_end(argList);
}
void LOGINFO(const char* a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
cMCLogger::GetInstance()->Info( a_Format, argList );
va_end(argList);
}
void LOGWARN(const char* a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
cMCLogger::GetInstance()->Warn( a_Format, argList );
va_end(argList);
}
void LOGERROR(const char* a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
cMCLogger::GetInstance()->Error( a_Format, argList );
va_end(argList);
}

39
source/cMCLogger.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#ifndef _WIN32
#include <stdarg.h>
#endif
class cCriticalSection;
class cLog;
class cMCLogger //tolua_export
{ //tolua_export
private:
#ifdef _WIN32
typedef char* va_list;
#endif
public: //tolua_export
cMCLogger();
cMCLogger( char* a_File ); //tolua_export
~cMCLogger(); //tolua_export
void Log(const char* a_Format, va_list a_ArgList);
void Info(const char* a_Format, va_list a_ArgList);
void Warn(const char* a_Format, va_list a_ArgList);
void Error(const char* a_Format, va_list a_ArgList);
void LogSimple(const char* a_Text, int a_LogType = 0 ); //tolua_export
static cMCLogger* GetInstance();
private:
void SetColor( unsigned char a_Color );
cCriticalSection* m_CriticalSection;
cLog* m_Log;
static cMCLogger* s_MCLogger;
}; //tolua_export
extern void LOG(const char* a_Format, ...);
extern void LOGINFO(const char* a_Format, ...);
extern void LOGWARN(const char* a_Format, ...);
extern void LOGERROR(const char* a_Format, ...);

24
source/cMakeDir.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "cMakeDir.h"
#ifndef _WIN32
//#include <cstring> // If something is missing, uncomment some of these!
//#include <cstdlib>
//#include <stdio.h>
#include <sys/stat.h> // for mkdir
//#include <sys/types.h>
#else
#include <Windows.h>
#endif
void cMakeDir::MakeDir( const char* a_Directory )
{
#ifdef _WIN32
SECURITY_ATTRIBUTES Attrib;
Attrib.nLength = sizeof(SECURITY_ATTRIBUTES);
Attrib.lpSecurityDescriptor = NULL;
Attrib.bInheritHandle = false;
::CreateDirectory("world", &Attrib);
#else
mkdir("world", S_IRWXU | S_IRWXG | S_IRWXO);
#endif
}

7
source/cMakeDir.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
class cMakeDir
{
public:
static void MakeDir( const char* a_Directory );
};

609
source/cMonster.cpp Normal file
View File

@ -0,0 +1,609 @@
#include "cMonster.h"
#include "cRoot.h"
#include "cServer.h"
#include "cClientHandle.h"
#include "cWorld.h"
#include "cMCLogger.h"
#include "cChunk.h"
#include "cPlayer.h"
#include "BlockID.h"
#include "Defines.h"
#include "packets/cPacket_SpawnMob.h"
#include "packets/cPacket_EntityLook.h"
#include "packets/cPacket_TeleportEntity.h"
#include "packets/cPacket_RelativeEntityMoveLook.h"
#include "packets/cPacket_RelativeEntityMove.h"
#include "packets/cPacket_Metadata.h"
#include "Vector3f.h"
#include "Vector3i.h"
#include "Vector3d.h"
#include "cTracer.h"
#include "../iniFile/iniFile.h"
#ifndef _WIN32
#include <cstdlib> // rand
#include <unistd.h>
#include <string.h>
#endif
#include <string>
cMonster::cMonster()
: m_Target(0)
, m_Destination( new Vector3f() )
, m_bMovingToDestination(false)
, m_Speed( new Vector3f() )
, m_DestinationTime( 0 )
, m_Gravity( -9.81 )
, m_bOnGround( false )
, m_DestroyTimer( 0 )
, m_Jump(0)
, m_MobType( 0 )
, m_EMState(IDLE)
, m_SightDistance(25)
,m_SeePlayerInterval (0)
,m_EMPersonality(AGGRESSIVE)
,m_AttackDamage(1.0)
,m_AttackRange(5.0)
,m_AttackInterval(0)
,m_AttackRate(3)
,m_bPassiveAggressive(false)
,idle_interval(0)
,m_bBurnable(true)
,m_EMMetaState(NORMAL)
,m_FireDamageInterval(0)
,m_BurnPeriod(0)
{
LOG("cMonster::cMonster()");
LOG("In state: %s",GetState());
m_Health = 10;
int RandVal = rand() % 4;
if( RandVal == 0 )
m_MobType = 90; // Pig
else if( RandVal == 1 )
m_MobType = 91; // Sheep
else if( RandVal == 2 )
m_MobType = 92; // Cow
else
m_MobType = 93; // Hen
}
cMonster::~cMonster()
{
LOG("cMonster::~cMonster()");
delete m_Destination;
delete m_Speed;
}
bool cMonster::IsA( const char* a_EntityType )
{
//LOG("IsA( cMonster ) : %s", a_EntityType);
if( strcmp( a_EntityType, "cMonster" ) == 0 ) return true;
return cPawn::IsA( a_EntityType );
}
void cMonster::SpawnOn( cClientHandle* a_Target )
{
LOG("Spawn monster on client");
cPacket_SpawnMob Spawn;
Spawn.m_UniqueID = GetUniqueID();
Spawn.m_Type = m_MobType;
*Spawn.m_Pos = Vector3i((*m_Pos)*32);
Spawn.m_Yaw = 0;
Spawn.m_Pitch = 0;
Spawn.m_MetaDataSize = 1;
Spawn.m_MetaData = new char[Spawn.m_MetaDataSize];
Spawn.m_MetaData[0] = 0x7f; // not on fire/crouching/riding
//Spawn.m_MetaData[1] = 0x7f; // terminator
if( a_Target == 0 )
{
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
Chunk->Broadcast( Spawn );
}
else
{
a_Target->Send( Spawn );
}
}
void cMonster::MoveToPosition( const Vector3f & a_Position )
{
m_bMovingToDestination = true;
*m_Destination = a_Position;
}
bool cMonster::ReachedDestination()
{
Vector3f Distance = (*m_Destination) - Vector3f( m_Pos );
if( Distance.SqrLength() < 2.f )
return true;
return false;
}
void cMonster::Tick(float a_Dt)
{
if( m_Health <= 0 )
{
m_DestroyTimer += a_Dt/1000;
if( m_DestroyTimer > 1 )
{
Destroy();
}
return;
}
//a_Dt/=1000;
a_Dt/=1000;
if( m_bMovingToDestination )
{
Vector3f Pos( m_Pos );
Vector3f Distance = *m_Destination - Pos;
if( !ReachedDestination() )
{
Distance.y = 0;
Distance.Normalize();
Distance*=3;
m_Speed->x = Distance.x;
m_Speed->z = Distance.z;
}
else
{
m_bMovingToDestination = false;
}
if( m_Speed->SqrLength() > 0.f )
{
if( m_bOnGround )
{
Vector3f NormSpeed = m_Speed->NormalizeCopy();
Vector3f NextBlock = Vector3f( *m_Pos ) + NormSpeed;
double NextHeight = (double)cRoot::Get()->GetWorld()->GetHeight( (int)NextBlock.x, (int)NextBlock.z );
if( NextHeight > m_Pos->y - 1.2 && NextHeight - m_Pos->y < 2.5 )
{
m_bOnGround = false;
m_Speed->y = 7.f; // Jump!!
}
}
}
}
HandlePhysics( a_Dt );
ReplicateMovement();
Vector3f Distance = *m_Destination - Vector3f( m_Pos );
if( Distance.SqrLength() > 0.1f )
{
float Rotation, Pitch;
Distance.Normalize();
VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch );
SetRotation( Rotation );
SetPitch( Pitch );
}
CheckMetaDataBurn(); //Check to see if Enemy should burn based on block they are on
if(m_EMMetaState == BURNING) {
InStateBurning(a_Dt);
}
if(m_EMState == IDLE) { //If enemy passive we ignore checks for player visibility
InStateIdle(a_Dt);
}
if(m_EMState == CHASING) { //If we do not see a player anymore skip chasing action
InStateChasing(a_Dt);
}
if(m_EMState == ESCAPING) {
InStateEscaping(a_Dt);
}
m_SeePlayerInterval += a_Dt;
if(m_SeePlayerInterval > 1) {
int rem = rand()%3 + 1; //check most of the time but miss occasionally
//LOG("See Player Interval: %3.3f",m_SeePlayerInterval);
m_SeePlayerInterval = 0.0;
if(rem >= 2) {
if(m_EMState == IDLE && m_EMPersonality != PASSIVE) {
CheckEventSeePlayer();
return;
}
if(m_EMState == CHASING || m_EMState == ESCAPING){
CheckEventLostPlayer();
return;
}
}
}
}
void cMonster::ReplicateMovement()
{
cChunk* InChunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if( !InChunk ) return;
if(m_bDirtyOrientation && !m_bDirtyPosition)
{
cPacket_EntityLook EntityLook( this );
InChunk->Broadcast( EntityLook );
m_bDirtyOrientation = false;
}
if( m_bDirtyPosition )
{
float DiffX = (float)(GetPosX() - m_LastPosX );
float DiffY = (float)(GetPosY() - m_LastPosY );
float DiffZ = (float)(GetPosZ() - m_LastPosZ );
float SqrDist = DiffX*DiffX + DiffY*DiffY + DiffZ*DiffZ;
if( SqrDist > 4*4 // 4 blocks is max Relative Move
|| cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds
{
//LOG("Teleported %f", sqrtf(SqrDist) );
cPacket_TeleportEntity TeleportEntity( this );
InChunk->Broadcast( TeleportEntity );
m_TimeLastTeleportPacket = cWorld::GetTime();
}
else
{ // Relative move sucks balls! It's always wrong wtf!
if( m_bDirtyOrientation )
{
cPacket_RelativeEntityMoveLook RelativeEntityMoveLook;
RelativeEntityMoveLook.m_UniqueID = GetUniqueID();
RelativeEntityMoveLook.m_MoveX = (char)(DiffX*32);
RelativeEntityMoveLook.m_MoveY = (char)(DiffY*32);
RelativeEntityMoveLook.m_MoveZ = (char)(DiffZ*32);
RelativeEntityMoveLook.m_Yaw = (char)((GetRotation()/360.f)*256);
RelativeEntityMoveLook.m_Pitch = (char)((GetPitch()/360.f)*256);
InChunk->Broadcast( RelativeEntityMoveLook );
}
else
{
cPacket_RelativeEntityMove RelativeEntityMove;
RelativeEntityMove.m_UniqueID = GetUniqueID();
RelativeEntityMove.m_MoveX = (char)(DiffX*32);
RelativeEntityMove.m_MoveY = (char)(DiffY*32);
RelativeEntityMove.m_MoveZ = (char)(DiffZ*32);
InChunk->Broadcast( RelativeEntityMove );
}
}
m_LastPosX = GetPosX();
m_LastPosY = GetPosY();
m_LastPosZ = GetPosZ();
m_bDirtyPosition = false;
}
}
void cMonster::HandlePhysics(float a_Dt)
{
if( m_bOnGround ) // check if it's still on the ground
{
cWorld* World = cRoot::Get()->GetWorld();
if( World->GetBlock( (int)m_Pos->x, (int)m_Pos->y -1, (int)m_Pos->z ) == E_BLOCK_AIR )
{
m_bOnGround = false;
}
if( World->GetBlock( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z ) != E_BLOCK_AIR ) // If in ground itself, push it out
{
m_bOnGround = true;
m_Pos->y += 0.2;
m_bDirtyPosition = true;
}
m_Speed->x *= 0.7f/(1+a_Dt);
if( fabs(m_Speed->x) < 0.05 ) m_Speed->x = 0;
m_Speed->z *= 0.7f/(1+a_Dt);
if( fabs(m_Speed->z) < 0.05 ) m_Speed->z = 0;
}
if( !m_bOnGround )
{
float Gravity = -9.81f*a_Dt;
m_Speed->y += Gravity;
}
if( m_Speed->SqrLength() > 0.f )
{
cTracer Tracer( cRoot::Get()->GetWorld() );
int Ret = Tracer.Trace( *m_Pos, *m_Speed, 2 );
if( Ret ) // Oh noez! we hit something
{
// Set to hit position
if( (*Tracer.RealHit - Vector3f(*m_Pos)).SqrLength() <= ( *m_Speed * a_Dt ).SqrLength() )
{
if( Ret == 1 )
{
if( Tracer.HitNormal->x != 0.f ) m_Speed->x = 0.f;
if( Tracer.HitNormal->y != 0.f ) m_Speed->y = 0.f;
if( Tracer.HitNormal->z != 0.f ) m_Speed->z = 0.f;
if( Tracer.HitNormal->y > 0 ) // means on ground
{
m_bOnGround = true;
}
}
*m_Pos = Tracer.RealHit;
*m_Pos += *Tracer.HitNormal * 0.2;
}
else
*m_Pos += *m_Speed*a_Dt;
}
else
{ // We didn't hit anything, so move =]
*m_Pos += *m_Speed*a_Dt;
}
m_bDirtyPosition = true;
}
}
void cMonster::TakeDamage( int a_Damage, cEntity* a_Instigator )
{
cPawn::TakeDamage( a_Damage, a_Instigator );
m_Target = a_Instigator;
AddReference( m_Target );
if(m_EMPersonality == AGGRESSIVE) {
m_EMState = CHASING;
}
if(m_EMPersonality == COWARDLY || m_EMPersonality == PASSIVE) {
//m_bPassiveAggressive can be set so if the monster based on time of day for example
//so the monster will only attack if provoked
m_EMState = (m_bPassiveAggressive)? CHASING : ESCAPING;
}
//LOG("Take damage");
}
void cMonster::KilledBy( cEntity* a_Killer )
{
cPawn::KilledBy( a_Killer );
m_DestroyTimer = 0;
}
//----State Logic
const char *cMonster::GetState() {
switch(m_EMState) {
case IDLE:
return "Idle";
break;
case ATTACKING:
return "Attacking";
break;
case CHASING:
return "Chasing";
break;
default:
return "Unknown";
break;
}
}
//for debugging
void cMonster::SetState(const char* a_str) {
std::string str = a_str;
if(str.compare("Idle") == 0 ) {
m_EMState = IDLE;
} else if(str.compare("Attacking") == 0 ) {
m_EMState = ATTACKING;
} else if(str.compare("Chasing") == 0 ) {
m_EMState = CHASING;
} else {
printf("Invalid State");
}
}
//Checks to see if EventSeePlayer should be fired
//monster sez: Do I see the player
void cMonster::CheckEventSeePlayer() {
//LOG("Checking if I see any players");
cMonster::ListClosePlayers(this);
}
void cMonster::CheckEventLostPlayer() {
Vector3f pos;
cTracer LineOfSight(cRoot::Get()->GetWorld() );
//LOG("Checking if I lost my enemy");
if(m_Target != 0) {
pos = m_Target->GetPosition();
if((pos - *m_Pos).Length() > m_SightDistance || LineOfSight.Trace(*m_Pos,(pos - *m_Pos), (int)(pos - *m_Pos).Length())){
//LOG("Losing Player: %5.5f",(pos - *m_Pos).Length());
EventLosePlayer();
}
} else {
LOG("Enemy went poof");
EventLosePlayer();
}
}
//What to do if player is seen
//default to change state to chasing
void cMonster::EventSeePlayer(cEntity *a_SeenPlayer) {
m_Target = a_SeenPlayer;
AddReference( m_Target );
if(m_EMPersonality == AGGRESSIVE) {
m_EMState = CHASING;
}
if(m_EMPersonality == COWARDLY) {
m_EMState = ESCAPING;
}
//LOG("Saw Player: %s",GetState());
}
void cMonster::EventLosePlayer(){
Dereference(m_Target);
m_Target = 0;
//LOG("Lost Player");
m_EMState = IDLE;
}
//What to do if in Idle State
void cMonster::InStateIdle(float a_Dt) {
idle_interval += a_Dt;
if(idle_interval > 1) { //at this interval the results are predictable
int rem = rand()%6 + 1;
//LOG("Moving: int: %3.3f rem: %i",idle_interval,rem);
idle_interval = 0;
Vector3f Dist;
Dist.x = (float)((rand()%11)-5);
Dist.z = (float)((rand()%11)-5);
if( Dist.SqrLength() > 2 && rem >= 3)
{
m_Destination->x = (float)(m_Pos->x + Dist.x);
m_Destination->z = (float)(m_Pos->z + Dist.z);
m_Destination->y = (float)cRoot::Get()->GetWorld()->GetHeight( (int)m_Destination->x, (int)m_Destination->z ) + 1.2f;
MoveToPosition( *m_Destination );
}
}
}
//What to do if On fire
void cMonster::InStateBurning(float a_Dt) {
m_FireDamageInterval += a_Dt;
char block = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z );
char bblock = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y -1, (int)m_Pos->z );
if(m_FireDamageInterval > 1) {
m_FireDamageInterval = 0;
int rem = rand()%3 + 1; //Burn most of the time
if(rem >= 2) {
//printf("OUCH burning!!!\n");
TakeDamage(1, this);
}
m_BurnPeriod++;
if(block == E_BLOCK_LAVA || block == E_BLOCK_STATIONARY_LAVA || block == E_BLOCK_FIRE
|| bblock == E_BLOCK_LAVA || bblock == E_BLOCK_STATIONARY_LAVA || bblock == E_BLOCK_FIRE)
m_BurnPeriod = 0;
if(m_BurnPeriod > 5) {
cChunk* InChunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
m_EMMetaState = NORMAL;
cPacket_Metadata md(NORMAL, GetUniqueID());
//md.m_UniqueID = GetUniqueID();
InChunk->Broadcast(md);
m_BurnPeriod = 0;
}
}
}
//What to do if in Chasing State
//This state should always be defined in each child class
void cMonster::InStateChasing(float a_Dt) {
(void)a_Dt;
}
//What to do if in Escaping State
void cMonster::InStateEscaping(float a_Dt) {
(void)a_Dt;
if(m_Target) {
Vector3d newloc = *m_Pos;
newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance);
newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance);
MoveToPosition(newloc);
} else {
m_EMState = IDLE; //this shouldnt be required but just to be safe
}
}
//Do attack here
//a_Dt is passed so we can set attack rate
void cMonster::Attack(float a_Dt) {
m_AttackInterval += a_Dt*m_AttackRate;
if(m_Target != 0 && m_AttackInterval > 3.0) { //Setting this higher gives us more wiggle room for attackrate
//LOG("ATTACK!");
m_AttackInterval = 0.0;
((cPawn *)m_Target)->TakeDamage((int)m_AttackDamage,this);
}
}
//----Change Entity MetaData
void cMonster::CheckMetaDataBurn() {
char block = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z );
char bblock = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y -1, (int)m_Pos->z );
if(m_bBurnable && m_EMMetaState != BURNING && (block == E_BLOCK_LAVA || block == E_BLOCK_STATIONARY_LAVA || block == E_BLOCK_FIRE
|| bblock == E_BLOCK_LAVA || bblock == E_BLOCK_STATIONARY_LAVA || bblock == E_BLOCK_FIRE)) {
cChunk* InChunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if(!InChunk)
return;
//printf("I should burn");
m_EMMetaState = BURNING;
cPacket_Metadata md(BURNING,GetUniqueID());
InChunk->Broadcast(md);
}
}
//----Debug
void cMonster::ListMonsters() {
cWorld::EntityList Entities = cRoot::Get()->GetWorld()->GetEntities();
cRoot::Get()->GetWorld()->LockEntities();
for( cWorld::EntityList::iterator itr = Entities.begin(); itr != Entities.end(); ++itr) {
if((*itr)->GetEntityType() == cEntity::E_ENTITY){
LOG("In state: %s type: %i attack rate: %i",((cMonster *)(*itr))->GetState(), ((cMonster *)(*itr))->GetMobType(),((cMonster *)(*itr))->GetAttackRate());
}
}
cRoot::Get()->GetWorld()->UnlockEntities();
}
//Checks for Players close by and if they are visible
void cMonster::ListClosePlayers(cMonster *m) {
int tries = 0;
cTracer LineOfSight(cRoot::Get()->GetWorld() );
cWorld::EntityList Entities = cRoot::Get()->GetWorld()->GetEntities();
for( cWorld::EntityList::iterator itr = Entities.begin(); itr != Entities.end(); ++itr) {
tries++;
if((*itr)->GetEntityType() == cEntity::E_PLAYER){
Vector3f pos = (*itr)->GetPosition();
if((pos - *(m->m_Pos)).Length() <= m->m_SightDistance){
if(!LineOfSight.Trace(*(m->m_Pos),(pos - *(m->m_Pos)),(int)(pos - *(m->m_Pos)).Length())){
//LOG("I SEE PLAYER !!!!!!!!!!!!!!!!!");
m->EventSeePlayer(*itr);
return; //get the first one in sight later we can reiterate and check
//for the closest out of all that match and make it more realistic
}
}
}
if(tries > 100) {
//LOG("I Give Up");
m->EventLosePlayer();
return;
}
}
}
void cMonster::GetMonsterConfig(const char* pm_name) {
(void)pm_name;
}
void cMonster::SetAttackRate(int ar) {
m_AttackRate = (float)ar;
}
void cMonster::SetAttackRange(float ar) {
m_AttackRange = ar;
}
void cMonster::SetAttackDamage(float ad) {
m_AttackDamage = ad;
}
void cMonster::SetSightDistance(float sd) {
m_SightDistance = sd;
}

85
source/cMonster.h Normal file
View File

@ -0,0 +1,85 @@
#pragma once
#include "cPawn.h"
class Vector3f;
class cClientHandle;
class cMonster : public cPawn //tolua_export
{ //tolua_export
public:
cMonster();
virtual ~cMonster();
virtual bool IsA( const char* a_EntityType );
virtual void SpawnOn( cClientHandle* a_Target );
virtual void Tick(float a_Dt);
virtual void HandlePhysics(float a_Dt);
virtual void ReplicateMovement();
virtual void TakeDamage( int a_Damage, cEntity* a_Instigator );
virtual void KilledBy( cEntity* a_Killer );
virtual void MoveToPosition( const Vector3f & a_Position );
virtual bool ReachedDestination();
const char *GetState();
void SetState(const char* str);
static void ListMonsters();
virtual void CheckEventSeePlayer();
virtual void EventSeePlayer(cEntity *);
float m_SightDistance;
static void ListClosePlayers(cMonster *);
virtual void GetMonsterConfig(const char* pm_name);
virtual void EventLosePlayer();
virtual void CheckEventLostPlayer();
virtual void InStateIdle(float a_Dt);
virtual void InStateChasing(float a_Dt);
virtual void InStateEscaping(float a_Dt);
virtual void InStateBurning(float a_Dt);
virtual void Attack(float a_Dt);
int GetMobType() {return m_MobType;}
int GetAttackRate(){return (int)m_AttackRate;}
void SetAttackRate(int ar);
void SetAttackRange(float ar);
void SetAttackDamage(float ad);
void SetSightDistance(float sd);
virtual void CheckMetaDataBurn();
enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState;
enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality;
enum MMetaState{NORMAL,BURNING,CROUCHED,RIDING} m_EMMetaState;
protected:
cEntity* m_Target;
float m_AttackRate;
float idle_interval;
Vector3f* m_Destination;
bool m_bMovingToDestination;
bool m_bPassiveAggressive;
bool m_bBurnable;
Vector3f* m_Speed;
float m_DestinationTime;
float m_Gravity;
bool m_bOnGround;
float m_DestroyTimer;
float m_Jump;
char m_MobType;
float m_SeePlayerInterval;
float m_AttackDamage;
float m_AttackRange;
float m_AttackInterval;
float m_FireDamageInterval;
float m_BurnPeriod;
}; //tolua_export

93
source/cMonsterConfig.cpp Normal file
View File

@ -0,0 +1,93 @@
#include "cMonsterConfig.h"
#include "cMonster.h"
#include "../iniFile/iniFile.h"
#include <list>
#include <vector>
#include <cstdio>
//#include "../source/cprintf.h"
#include <string>
using namespace std;
extern std::vector<std::string> StringSplit(std::string str, std::string delim);
struct cMonsterConfig::sAttributesStruct
{
string m_name;
float m_SightDistance;
float m_AttackDamage;
float m_AttackRange;
float m_AttackRate;
};
struct cMonsterConfig::sMonsterConfigState
{
int TypeCount;
string MonsterTypes;
list< sAttributesStruct > AttributesList;
};
cMonsterConfig::cMonsterConfig(int TypeC)
: m_pState( new sMonsterConfigState )
{
m_pState->TypeCount = TypeC;
Initialize();
}
cMonsterConfig::~cMonsterConfig() {
delete m_pState;
}
void cMonsterConfig::Initialize() {
sAttributesStruct Attributes;
cIniFile SettingsIniFile("settings.ini");
cIniFile MonstersIniFile("monsters.ini");
if(!SettingsIniFile.ReadFile() || !MonstersIniFile.ReadFile()) {
printf("Error: Must have both settings.ini and monsters.ini to configure attributes\n\tusing default attributes \n");
return;
}
m_pState->MonsterTypes = SettingsIniFile.GetValue("Monsters","Types","");
if( m_pState->MonsterTypes.empty() ) {
printf("Error: No Monster types listed in config file, using default attributes \n");
return;
}
vector<string> SplitList = StringSplit(m_pState->MonsterTypes,",");
for(unsigned int i = 0; i < SplitList.size(); ++i) {
if(!SplitList[i].empty()) {
printf("Getting Attributes for: %s \n",SplitList[i].c_str());
Attributes.m_name = SplitList[i].c_str();
Attributes.m_AttackDamage = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(),"AttackDamage",0);
printf("Got AttackDamage: %3.3f \n",Attributes.m_AttackDamage);
Attributes.m_AttackRange = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(),"AttackRange",0);
printf("Got AttackRange: %3.3f \n",Attributes.m_AttackRange);
Attributes.m_SightDistance = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(),"SightDistance",0);
printf("Got SightDistance: %3.3f \n",Attributes.m_SightDistance);
Attributes.m_AttackRate = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(),"AttackRate",0);
printf("Got AttackRate: %3.3f \n",Attributes.m_AttackRate);
m_pState->AttributesList.push_front(Attributes);
}
}
}
void cMonsterConfig::AssignAttributes(cMonster *m, const char* n)
{
list<sAttributesStruct>::iterator itr;
for(itr = m_pState->AttributesList.begin(); itr != m_pState->AttributesList.end(); ++itr) {
if(itr->m_name.compare(n) == 0) {
//printf("found my attribs: %s :\n",itr->m_name.c_str());
m->SetAttackDamage(itr->m_AttackDamage);
m->SetAttackRange(itr->m_AttackRange);
m->SetSightDistance(itr->m_SightDistance);
m->SetAttackRate((int)itr->m_AttackRate);
}
}
}
cMonsterConfig *cMonsterConfig::Get() {
return this;
}

18
source/cMonsterConfig.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
class cMonster;
class cMonsterConfig
{
public:
cMonsterConfig(int TypeC);
~cMonsterConfig();
cMonsterConfig *Get();
void AssignAttributes(cMonster *m, const char* n);
private:
struct sAttributesStruct;
struct sMonsterConfigState;
sMonsterConfigState* m_pState;
void Initialize();
};

253
source/cNoise.cpp Normal file
View File

@ -0,0 +1,253 @@
#include "cNoise.h"
#include <math.h>
#define FAST_FLOOR( x ) ( (x) < 0 ? ((int)x)-1 : ((int)x) )
cNoise::cNoise( unsigned int a_Seed )
: m_Seed( a_Seed )
{
}
cNoise::~cNoise()
{
}
/****************
* Random value generator
**/
float cNoise::IntNoise( int a_X ) const
{
int x = ((a_X*m_Seed)<<13) ^ a_X;
return ( 1.0f - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
}
float cNoise::IntNoise2D( int a_X, int a_Y ) const
{
int n = a_X + a_Y * 57 + m_Seed*57*57;
n = (n<<13) ^ n;
return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
}
float cNoise::IntNoise3D( int a_X, int a_Y, int a_Z ) const
{
int n = a_X + a_Y * 57 + a_Z * 57*57 + m_Seed*57*57*57;
n = (n<<13) ^ n;
return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
}
/***************
* Interpolated (and 1 smoothed) noise in 1-dimension
**/
float cNoise::LinearNoise1D( float a_X ) const
{
int BaseX = FAST_FLOOR( a_X );
float FracX = (a_X) - BaseX;
return LinearInterpolate( IntNoise( BaseX ), IntNoise( BaseX+1 ), FracX);
}
float cNoise::CosineNoise1D( float a_X ) const
{
int BaseX = FAST_FLOOR( a_X );
float FracX = (a_X) - BaseX;
return CosineInterpolate( IntNoise( BaseX ), IntNoise( BaseX+1 ), FracX);
}
float cNoise::CubicNoise1D( float a_X ) const
{
int BaseX = FAST_FLOOR( a_X );
float FracX = (a_X) - BaseX;
return CubicInterpolate( IntNoise( BaseX-1 ), IntNoise( BaseX ), IntNoise( BaseX+1 ), IntNoise( BaseX+2 ), FracX);
}
float cNoise::SmoothNoise1D( int a_X ) const
{
return IntNoise(a_X)/2 + IntNoise(a_X-1)/4 + IntNoise(a_X+1)/4;
}
/******************
* Interpolated (and 1 smoothed) noise in 2-dimensions
**/
float cNoise::LinearNoise2D( float a_X, float a_Y ) const
{
const int BaseX = FAST_FLOOR( a_X );
const int BaseY = FAST_FLOOR( a_Y );
const float tl = IntNoise2D( BaseX, BaseY );
const float tr = IntNoise2D( BaseX+1, BaseY );
const float bl = IntNoise2D( BaseX, BaseY+1 );
const float br = IntNoise2D( BaseX+1, BaseY+1 );
const float FracX = (a_X) - BaseX;
const float interp1 = LinearInterpolate( tl, tr, FracX );
const float interp2 = LinearInterpolate( bl, br, FracX );
const float FracY = (a_Y) - BaseY;
return LinearInterpolate( interp1, interp2, FracY );
}
float cNoise::CosineNoise2D( float a_X, float a_Y ) const
{
const int BaseX = FAST_FLOOR( a_X );
const int BaseY = FAST_FLOOR( a_Y );
const float tl = IntNoise2D( BaseX, BaseY );
const float tr = IntNoise2D( BaseX+1, BaseY );
const float bl = IntNoise2D( BaseX, BaseY+1 );
const float br = IntNoise2D( BaseX+1, BaseY+1 );
const float FracX = (a_X) - BaseX;
const float interp1 = CosineInterpolate( tl, tr, FracX );
const float interp2 = CosineInterpolate( bl, br, FracX );
const float FracY = (a_Y) - BaseY;
return CosineInterpolate( interp1, interp2, FracY );
}
float cNoise::CubicNoise2D( float a_X, float a_Y ) const
{
const int BaseX = FAST_FLOOR( a_X );
const int BaseY = FAST_FLOOR( a_Y );
const float points[4][4] = {
IntNoise2D( BaseX-1, BaseY-1 ), IntNoise2D( BaseX, BaseY-1 ), IntNoise2D( BaseX+1, BaseY-1 ), IntNoise2D( BaseX+2, BaseY-1 ),
IntNoise2D( BaseX-1, BaseY ), IntNoise2D( BaseX, BaseY ), IntNoise2D( BaseX+1, BaseY ), IntNoise2D( BaseX+2, BaseY ),
IntNoise2D( BaseX-1, BaseY+1 ), IntNoise2D( BaseX, BaseY+1 ), IntNoise2D( BaseX+1, BaseY+1 ), IntNoise2D( BaseX+2, BaseY+1 ),
IntNoise2D( BaseX-1, BaseY+2 ), IntNoise2D( BaseX, BaseY+2 ), IntNoise2D( BaseX+1, BaseY+2 ), IntNoise2D( BaseX+2, BaseY+2 ),
};
const float FracX = (a_X) - BaseX;
const float interp1 = CubicInterpolate( points[0][0], points[0][1], points[0][2], points[0][3], FracX );
const float interp2 = CubicInterpolate( points[1][0], points[1][1], points[1][2], points[1][3], FracX );
const float interp3 = CubicInterpolate( points[2][0], points[2][1], points[2][2], points[2][3], FracX );
const float interp4 = CubicInterpolate( points[3][0], points[3][1], points[3][2], points[3][3], FracX );
const float FracY = (a_Y) - BaseY;
return CubicInterpolate( interp1, interp2, interp3, interp4, FracY );
}
/******************
* Interpolated (and 1 smoothed) noise in 3-dimensions
**/
float cNoise::CosineNoise3D( float a_X, float a_Y, float a_Z ) const
{
const int BaseX = FAST_FLOOR( a_X );
const int BaseY = FAST_FLOOR( a_Y );
const int BaseZ = FAST_FLOOR( a_Z );
const float ftl = IntNoise3D( BaseX, BaseY, BaseZ );
const float ftr = IntNoise3D( BaseX+1, BaseY, BaseZ );
const float fbl = IntNoise3D( BaseX, BaseY+1, BaseZ );
const float fbr = IntNoise3D( BaseX+1, BaseY+1, BaseZ );
const float btl = IntNoise3D( BaseX, BaseY, BaseZ+1 );
const float btr = IntNoise3D( BaseX+1, BaseY, BaseZ+1 );
const float bbl = IntNoise3D( BaseX, BaseY+1, BaseZ+1 );
const float bbr = IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 );
const float FracX = (a_X) - BaseX;
const float finterp1 = CosineInterpolate( ftl, ftr, FracX );
const float finterp2 = CosineInterpolate( fbl, fbr, FracX );
const float binterp1 = CosineInterpolate( btl, btr, FracX );
const float binterp2 = CosineInterpolate( bbl, bbr, FracX );
const float FracY = (a_Y) - BaseY;
const float interp1 = CosineInterpolate( finterp1, finterp2, FracY );
const float interp2 = CosineInterpolate( binterp1, binterp2, FracY );
const float FracZ = (a_Z) - BaseZ;
return CosineInterpolate( interp1, interp2, FracZ );
}
float cNoise::CubicNoise3D( float a_X, float a_Y, float a_Z ) const
{
const int BaseX = FAST_FLOOR( a_X );
const int BaseY = FAST_FLOOR( a_Y );
const int BaseZ = FAST_FLOOR( a_Z );
const float points1[4][4] = {
IntNoise3D( BaseX-1, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ-1 ),
IntNoise3D( BaseX-1, BaseY, BaseZ-1 ), IntNoise3D( BaseX, BaseY, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY, BaseZ-1 ),
IntNoise3D( BaseX-1, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ-1 ),
IntNoise3D( BaseX-1, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ-1 ),
};
const float FracX = (a_X) - BaseX;
const float x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX );
const float x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX );
const float x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX );
const float x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX );
const float points2[4][4] = {
IntNoise3D( BaseX-1, BaseY-1, BaseZ ), IntNoise3D( BaseX, BaseY-1, BaseZ ), IntNoise3D( BaseX+1, BaseY-1, BaseZ ), IntNoise3D( BaseX+2, BaseY-1, BaseZ ),
IntNoise3D( BaseX-1, BaseY, BaseZ ), IntNoise3D( BaseX, BaseY, BaseZ ), IntNoise3D( BaseX+1, BaseY, BaseZ ), IntNoise3D( BaseX+2, BaseY, BaseZ ),
IntNoise3D( BaseX-1, BaseY+1, BaseZ ), IntNoise3D( BaseX, BaseY+1, BaseZ ), IntNoise3D( BaseX+1, BaseY+1, BaseZ ), IntNoise3D( BaseX+2, BaseY+1, BaseZ ),
IntNoise3D( BaseX-1, BaseY+2, BaseZ ), IntNoise3D( BaseX, BaseY+2, BaseZ ), IntNoise3D( BaseX+1, BaseY+2, BaseZ ), IntNoise3D( BaseX+2, BaseY+2, BaseZ ),
};
const float x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX );
const float x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX );
const float x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX );
const float x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX );
const float points3[4][4] = {
IntNoise3D( BaseX-1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+1 ),
IntNoise3D( BaseX-1, BaseY, BaseZ+1 ), IntNoise3D( BaseX, BaseY, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY, BaseZ+1 ),
IntNoise3D( BaseX-1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+1 ),
IntNoise3D( BaseX-1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+1 ),
};
const float x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX );
const float x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX );
const float x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX );
const float x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX );
const float points4[4][4] = {
IntNoise3D( BaseX-1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2 ),
IntNoise3D( BaseX-1, BaseY, BaseZ+2 ), IntNoise3D( BaseX, BaseY, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY, BaseZ+2 ),
IntNoise3D( BaseX-1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2 ),
IntNoise3D( BaseX-1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2 ),
};
const float x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX );
const float x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX );
const float x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX );
const float x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX );
const float FracY = (a_Y) - BaseY;
const float yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY );
const float yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY );
const float yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY );
const float yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY );
const float FracZ = (a_Z) - BaseZ;
return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ );
}
/******************
* Private
**/
float cNoise::CubicInterpolate( float a_A, float a_B, float a_C, float a_D, float a_Pct ) const
{
float P = (a_D - a_C) - (a_A - a_B);
float Q = (a_A - a_B) - P;
float R = a_C - a_A;
float S = a_B;
return P*(a_Pct*a_Pct*a_Pct) + Q*(a_Pct*a_Pct) + R*a_Pct + S;
}
float cNoise::CosineInterpolate( float a_A, float a_B, float a_Pct ) const
{
const float ft = a_Pct * 3.1415927f;
const float f = (1.f - cosf(ft)) * 0.5f;
return a_A*(1-f) + a_B*f;
}
float cNoise::LinearInterpolate( float a_A, float a_B, float a_Pct ) const
{
return a_A*(1.f-a_Pct) + a_B*a_Pct;
}

33
source/cNoise.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
class cNoise
{
public:
cNoise( unsigned int a_Seed );
~cNoise();
float IntNoise( int a_X ) const;
float IntNoise2D( int a_X, int a_Y ) const;
float IntNoise3D( int a_X, int a_Y, int a_Z ) const;
float LinearNoise1D( float a_X ) const;
float CosineNoise1D( float a_X ) const;
float CubicNoise1D( float a_X ) const;
float SmoothNoise1D( int a_X ) const;
float LinearNoise2D( float a_X, float a_Y ) const;
float CosineNoise2D( float a_X, float a_Y ) const;
float CubicNoise2D( float a_X, float a_Y ) const;
float CosineNoise3D( float a_X, float a_Y, float a_Z ) const;
float CubicNoise3D( float a_X, float a_Y, float a_Z ) const;
void SetSeed( unsigned int a_Seed ) { m_Seed = a_Seed; }
private:
float CubicInterpolate( float a_A, float a_B, float a_C, float a_D, float a_Pct ) const;
float CosineInterpolate( float a_A, float a_B, float a_Pct ) const;
float LinearInterpolate( float a_A, float a_B, float a_Pct ) const;
unsigned int m_Seed;
};

96
source/cPawn.cpp Normal file
View File

@ -0,0 +1,96 @@
#include "cPawn.h"
#include "cRoot.h"
#include "cServer.h"
#include "cWorld.h"
#include "cPlayer.h"
#include "cChunk.h"
#include "cMCLogger.h"
#include "cPluginManager.h"
#include "packets/cPacket_TeleportEntity.h"
#include "packets/cPacket_EntityStatus.h"
CLASS_DEFINITION( cPawn, cEntity )
cPawn::cPawn()
: cEntity( 0, 0, 0 )
, m_LastPosX( 0.0 )
, m_LastPosY( 0.0 )
, m_LastPosZ( 0.0 )
, m_TimeLastTeleportPacket( 0.f )
{
m_Health = 20;
}
cPawn::~cPawn()
{
}
void cPawn::Heal( int a_Health )
{
(void)a_Health;
}
void cPawn::TakeDamage( int a_Damage, cEntity* a_Instigator )
{
TakeDamageInfo TDI;
TDI.Damage = a_Damage;
TDI.Instigator = a_Instigator;
cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_TAKE_DAMAGE, 2, this, &TDI );
if( TDI.Damage == 0 ) return;
if( m_Health <= 0 ) return; // Can't take damage if already dead
m_Health -= (short)TDI.Damage;
if( m_Health < 0 ) m_Health = 0;
cPacket_EntityStatus Status;
Status.m_UniqueID = GetUniqueID();
Status.m_Status = cPacket_EntityStatus::STATUS_TAKEDAMAGE;
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if( Chunk )
Chunk->Broadcast( Status );
if( m_Health <= 0 )
KilledBy( TDI.Instigator );
}
void cPawn::KilledBy( cEntity* a_Killer )
{
m_Health = 0;
if( cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_KILLED, 2, this, a_Killer ) )
{
return; // Give plugins a chance to 'unkill' the pawn.
}
cPacket_EntityStatus Status;
Status.m_UniqueID = GetUniqueID();
Status.m_Status = cPacket_EntityStatus::STATUS_DIE;
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if( Chunk )
Chunk->Broadcast( Status ); // Die
}
void cPawn::TeleportTo( cEntity* a_Entity )
{
TeleportTo( a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ() );
}
void cPawn::TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ )
{
SetPosition( a_PosX, a_PosY, a_PosZ );
cPacket_TeleportEntity TeleportEntity( this );
if( IsA("cPlayer") )
{
cPlayer* Player = (cPlayer*)this;
cRoot::Get()->GetServer()->Broadcast( TeleportEntity, Player->GetClientHandle() );
}
else
{
cRoot::Get()->GetServer()->Broadcast( TeleportEntity );
}
}

31
source/cPawn.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "cEntity.h"
struct TakeDamageInfo //tolua_export
{ //tolua_export
int Damage; //tolua_export
cEntity* Instigator; //tolua_export
}; //tolua_export
class cPawn : public cEntity //tolua_export
{ //tolua_export
public:
CLASS_PROTOTYPE();
cPawn();
virtual ~cPawn();
virtual void TeleportTo( cEntity* a_Entity ); //tolua_export
virtual void TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export
void Heal( int a_Health ); //tolua_export
virtual void TakeDamage( int a_Damage, cEntity* a_Instigator ); //tolua_export
virtual void KilledBy( cEntity* a_Killer ); //tolua_export
int GetHealth() { return m_Health; } //tolua_export
protected:
short m_Health;
double m_LastPosX, m_LastPosY, m_LastPosZ;
float m_TimeLastTeleportPacket;
}; //tolua_export

229
source/cPickup.cpp Normal file
View File

@ -0,0 +1,229 @@
#ifndef _WIN32
#include <cstdlib>
#endif
#include "cPickup.h"
#include "cClientHandle.h"
#include "cInventory.h"
#include "cWorld.h"
#include "cServer.h"
#include "cPlayer.h"
#include "cPluginManager.h"
#include "cItem.h"
#include "cRoot.h"
#include "cMCLogger.h"
#include "cTracer.h"
#include "cChunk.h"
#include "packets/cPacket_TeleportEntity.h"
#include "packets/cPacket_PickupSpawn.h"
#include "packets/cPacket_CollectItem.h"
#include "Vector3d.h"
#include "Vector3f.h"
CLASS_DEFINITION( cPickup, cEntity )
cPickup::~cPickup()
{
delete m_Item;
}
cPickup::cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
: cEntity( ((double)(a_X))/32, ((double)(a_Y))/32, ((double)(a_Z))/32 )
, m_Speed( new Vector3f( a_SpeedX, a_SpeedY, a_SpeedZ ) )
, m_bOnGround( false )
, m_bReplicated( false )
, m_Timer( 0.f )
, m_Item( new cItem( a_Item ) )
, m_bCollected( false )
{
//LOG("New pickup: ID(%i) Amount(%i) Health(%i)", m_Item.m_ItemID, m_Item.m_ItemCount, m_Item.m_ItemHealth );
// Spawn it on clients
cPacket_PickupSpawn PickupSpawn;
PickupSpawn.m_UniqueID = m_UniqueID;
PickupSpawn.m_Item = (short)m_Item->m_ItemID;
PickupSpawn.m_Count = m_Item->m_ItemCount;
PickupSpawn.m_Health = m_Item->m_ItemHealth;
PickupSpawn.m_PosX = a_X;
PickupSpawn.m_PosY = a_Y;
PickupSpawn.m_PosZ = a_Z;
PickupSpawn.m_Rotation = (char)(m_Speed->x * 8);
PickupSpawn.m_Pitch = (char)(m_Speed->y * 8);
PickupSpawn.m_Roll = (char)(m_Speed->z * 8);
cRoot::Get()->GetServer()->Broadcast( PickupSpawn );
m_EntityType = E_PICKUP;
}
cPickup::cPickup(cPacket_PickupSpawn* a_PickupSpawnPacket)
: cEntity( ((double)a_PickupSpawnPacket->m_PosX)/32, ((double)a_PickupSpawnPacket->m_PosY)/32, ((double)a_PickupSpawnPacket->m_PosZ)/32 )
, m_bOnGround( false )
, m_bReplicated( false )
, m_Timer( 0.f )
, m_bCollected( false )
{
a_PickupSpawnPacket->m_UniqueID = m_UniqueID;
m_Item = new cItem();
m_Item->m_ItemID = (ENUM_ITEM_ID)a_PickupSpawnPacket->m_Item;
m_Item->m_ItemCount = a_PickupSpawnPacket->m_Count;
m_Item->m_ItemHealth = 0x0;
m_Speed->x = (float)(a_PickupSpawnPacket->m_Rotation) / 8;
m_Speed->y = (float)(a_PickupSpawnPacket->m_Pitch) / 8;
m_Speed->z = (float)(a_PickupSpawnPacket->m_Roll) / 8;
// Spawn it on clients
cRoot::Get()->GetServer()->Broadcast( *a_PickupSpawnPacket );
m_EntityType = E_PICKUP;
}
void cPickup::SpawnOn( cClientHandle* a_Target )
{
cPacket_PickupSpawn PickupSpawn;
PickupSpawn.m_UniqueID = m_UniqueID;
PickupSpawn.m_Item = (short)m_Item->m_ItemID;
PickupSpawn.m_Count = m_Item->m_ItemCount;
PickupSpawn.m_Health = m_Item->m_ItemHealth;
PickupSpawn.m_PosX = (int)(m_Pos->x * 32);
PickupSpawn.m_PosY = (int)(m_Pos->y * 32);
PickupSpawn.m_PosZ = (int)(m_Pos->z * 32);
PickupSpawn.m_Rotation = (char)(m_Speed->x * 8);
PickupSpawn.m_Pitch = (char)(m_Speed->y * 8);
PickupSpawn.m_Roll = (char)(m_Speed->z * 8);
a_Target->Send( PickupSpawn );
}
void cPickup::Tick(float a_Dt)
{
m_Timer+=a_Dt;
a_Dt = a_Dt / 1000.f;
if(m_bCollected)
{
if(m_Timer > 500.f) // 0.5 second
{
Destroy();
return;
}
}
if( m_Timer > 1000*60*5 ) // 5 minutes
{
Destroy();
return;
}
if( m_Pos->y < 0 ) // Out of this world!
{
Destroy();
return;
}
HandlePhysics( a_Dt );
if( !m_bReplicated || m_bDirtyPosition )
{
MoveToCorrectChunk();
m_bReplicated = true;
m_bDirtyPosition = false;
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if( Chunk )
{
cPacket_TeleportEntity TeleportEntity( this );
Chunk->Broadcast( TeleportEntity );
}
}
//printf("YSpeed: %f, OnGround: %i\n", m_SpeedY, m_bOnGround );
}
void cPickup::HandlePhysics(float a_Dt)
{
if( m_bOnGround ) // check if it's still on the ground
{
cWorld* World = cRoot::Get()->GetWorld();
int BlockX = (int)m_Pos->x;
if( m_Pos->x < 0 ) BlockX--;
int BlockZ = (int)m_Pos->z;
if( m_Pos->z < 0 ) BlockZ--;
if( World->GetBlock( BlockX, (int)m_Pos->y -1, BlockZ ) == E_BLOCK_AIR )
{
m_bOnGround = false;
}
if( World->GetBlock( BlockX, (int)m_Pos->y, BlockZ ) != E_BLOCK_AIR ) // If in ground itself, push it out
{
m_bOnGround = true;
m_Pos->y += 0.2;
m_bReplicated = false;
}
m_Speed->x *= 0.7f/(1+a_Dt);
if( fabs(m_Speed->x) < 0.05 ) m_Speed->x = 0;
m_Speed->z *= 0.7f/(1+a_Dt);
if( fabs(m_Speed->z) < 0.05 ) m_Speed->z = 0;
}
if( !m_bOnGround )
{
float Gravity = -9.81f*a_Dt;
m_Speed->y += Gravity;
cTracer Tracer( cRoot::Get()->GetWorld() );
int Ret = Tracer.Trace( *m_Pos, *m_Speed, 2 );
if( Ret ) // Oh noez! we hit something
{
// Set to hit position
if( (*Tracer.RealHit - Vector3f(*m_Pos)).SqrLength() <= ( *m_Speed * a_Dt ).SqrLength() )
{
m_bReplicated = false; // It's only interesting to replicate when we actually hit something...
if( Ret == 1 )
{
if( Tracer.HitNormal->x != 0.f ) m_Speed->x = 0.f;
if( Tracer.HitNormal->y != 0.f ) m_Speed->y = 0.f;
if( Tracer.HitNormal->z != 0.f ) m_Speed->z = 0.f;
if( Tracer.HitNormal->y > 0 ) // means on ground
{
m_bOnGround = true;
}
}
*m_Pos = Tracer.RealHit;
*m_Pos += *Tracer.HitNormal * 0.2;
}
else
*m_Pos += *m_Speed*a_Dt;
}
else
{ // We didn't hit anything, so move =]
*m_Pos += *m_Speed*a_Dt;
}
}
}
bool cPickup::CollectedBy( cPlayer* a_Dest )
{
if(m_bCollected) return false; // It's already collected!
if(m_Timer < 1000.f) return false; // Not old enough
if( cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_COLLECT_ITEM, 2, this, a_Dest ) ) return false;
if( a_Dest->GetInventory().AddItem( *m_Item ) )
{
cPacket_CollectItem CollectItem;
CollectItem.m_CollectedID = m_UniqueID;
CollectItem.m_CollectorID = a_Dest->GetUniqueID();
cRoot::Get()->GetServer()->Broadcast( CollectItem );
m_bCollected = true;
m_Timer = 0;
return true;
}
return false;
}

34
source/cPickup.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include "cEntity.h"
class cPacket_PickupSpawn;
class cPlayer;
class cItem;
class cPickup : public cEntity //tolua_export
{ //tolua_export
public:
CLASS_PROTOTYPE();
cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); //tolua_export
cPickup(cPacket_PickupSpawn* a_PickupSpawnPacket); //tolua_export
~cPickup(); //tolua_export
cItem* GetItem() { return m_Item; } //tolua_export
void SpawnOn( cClientHandle* a_Target );
virtual bool CollectedBy( cPlayer* a_Dest ); //tolua_export
void Tick(float a_Dt);
void HandlePhysics(float a_Dt);
private:
Vector3f* m_Speed;
bool m_bOnGround;
bool m_bReplicated;
float m_Timer;
cItem* m_Item;
bool m_bCollected;
};//tolua_export

713
source/cPlayer.cpp Normal file
View File

@ -0,0 +1,713 @@
#include "cPlayer.h"
#include "cServer.h"
#include "cInventory.h"
#include "cClientHandle.h"
#include "cWorld.h"
#include "cPickup.h"
#include "cPluginManager.h"
#include "cChunk.h"
#include "cMCLogger.h"
#include "cWindow.h"
#include "cBlockEntity.h"
#include "cGroupManager.h"
#include "cGroup.h"
#include "cChatColor.h"
#include "cItem.h"
#include "cTracer.h"
#include "cRoot.h"
#include "packets/cPacket_NamedEntitySpawn.h"
#include "packets/cPacket_EntityLook.h"
#include "packets/cPacket_TeleportEntity.h"
#include "packets/cPacket_RelativeEntityMove.h"
#include "packets/cPacket_RelativeEntityMoveLook.h"
#include "packets/cPacket_UpdateHealth.h"
#include "packets/cPacket_Respawn.h"
#include "packets/cPacket_PlayerPosition.h"
#include "packets/cPacket_DestroyEntity.h"
#include "packets/cPacket_Metadata.h"
#include "packets/cPacket_Chat.h"
#include "Vector3d.h"
#include "Vector3f.h"
#include "../iniFile/iniFile.h"
#ifndef _WIN32 // for mkdir
#include <sys/stat.h>
#include <sys/types.h>
#endif
extern std::vector< std::string > StringSplit( std::string str, std::string delim);
CLASS_DEFINITION( cPlayer, cPawn );
typedef std::map< std::string, bool > PermissionMap;
struct cPlayer::sPlayerState
{
PermissionMap ResolvedPermissions;
PermissionMap Permissions;
cPlayer::GroupList ResolvedGroups;
cPlayer::GroupList Groups;
std::string PlayerName;
};
cPlayer::cPlayer(cClientHandle* a_Client, const char* a_PlayerName)
: m_bBurnable(true)
, e_EPMetaState(NORMAL)
, m_bVisible( true )
, m_LastGroundHeight( 0 )
, m_bTouchGround( false )
, m_Stance( 0.0 )
, m_Inventory( 0 )
, m_CurrentWindow( 0 )
, m_TimeLastPickupCheck( 0.f )
, m_Color('-')
, m_FireDamageInterval(0)
, m_BurnPeriod(0)
, m_ClientHandle( a_Client )
, m_pState( new sPlayerState )
{
m_EntityType = E_PLAYER;
m_Inventory = new cInventory( this );
m_TimeLastTeleportPacket = cWorld::GetTime();
m_TimeLastPickupCheck = cWorld::GetTime();
m_pState->PlayerName = a_PlayerName;
m_bDirtyPosition = true; // So chunks are streamed to player at spawn
if( !LoadFromDisk() )
{
m_Inventory->Clear();
SetPosX( cRoot::Get()->GetWorld()->GetSpawnX() );
SetPosY( cRoot::Get()->GetWorld()->GetSpawnY() );
SetPosZ( cRoot::Get()->GetWorld()->GetSpawnZ() );
}
MoveToCorrectChunk();
cRoot::Get()->GetWorld()->AddPlayer( this );
}
cPlayer::~cPlayer(void)
{
SaveToDisk();
m_ClientHandle = 0;
CloseWindow();
if( m_Inventory )
{
delete m_Inventory;
m_Inventory = 0;
}
delete m_pState;
cRoot::Get()->GetWorld()->RemovePlayer( this );
}
void cPlayer::SpawnOn( cClientHandle* a_Target )
{
if( a_Target == m_ClientHandle || !m_bVisible ) return;
LOG("cPlayer::SpawnOn -> Sending %s to %s", m_pState->PlayerName.c_str(), (a_Target)?a_Target->GetUsername():"Everybody" );
cPacket_NamedEntitySpawn SpawnPacket;
SpawnPacket.m_UniqueID = m_UniqueID;
SpawnPacket.m_PlayerName = m_pState->PlayerName;
SpawnPacket.m_PosX = (int)(m_Pos->x * 32);
SpawnPacket.m_PosY = (int)(m_Pos->y * 32);
SpawnPacket.m_PosZ = (int)(m_Pos->z * 32);
SpawnPacket.m_Rotation = (char)((m_Rot->x/360.f)*256);
SpawnPacket.m_Pitch = (char)((m_Rot->y/360.f)*256);
SpawnPacket.m_CurrentItem = (short)m_Inventory->GetEquippedItem().m_ItemID;
if( a_Target == 0 )
{
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
Chunk->Broadcast( SpawnPacket, m_ClientHandle );
}
else
{
a_Target->Send( SpawnPacket );
}
}
void cPlayer::Tick(float a_Dt)
{
cChunk* InChunk = cRoot::Get()->GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
if(m_bDirtyOrientation && !m_bDirtyPosition)
{
cPacket_EntityLook EntityLook( this );
InChunk->Broadcast( EntityLook, m_ClientHandle );
m_bDirtyOrientation = false;
}
if(m_bDirtyPosition )
{
cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_PLAYER_MOVE, 1, this );
float DiffX = (float)(GetPosX() - m_LastPosX );
float DiffY = (float)(GetPosY() - m_LastPosY );
float DiffZ = (float)(GetPosZ() - m_LastPosZ );
float SqrDist = DiffX*DiffX + DiffY*DiffY + DiffZ*DiffZ;
if( SqrDist > 4*4 // 4 blocks is max Relative Move
|| cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds
{
//LOG("Teleported %f", sqrtf(SqrDist) );
cPacket_TeleportEntity TeleportEntity( this );
InChunk->Broadcast( TeleportEntity, m_ClientHandle );
m_TimeLastTeleportPacket = cWorld::GetTime();
}
else
{ // Relative move sucks balls! It's always wrong wtf!
if( m_bDirtyOrientation )
{
cPacket_RelativeEntityMoveLook RelativeEntityMoveLook;
RelativeEntityMoveLook.m_UniqueID = GetUniqueID();
RelativeEntityMoveLook.m_MoveX = (char)(DiffX*32);
RelativeEntityMoveLook.m_MoveY = (char)(DiffY*32);
RelativeEntityMoveLook.m_MoveZ = (char)(DiffZ*32);
RelativeEntityMoveLook.m_Yaw = (char)((GetRotation()/360.f)*256);
RelativeEntityMoveLook.m_Pitch = (char)((GetPitch()/360.f)*256);
InChunk->Broadcast( RelativeEntityMoveLook, m_ClientHandle );
}
else
{
cPacket_RelativeEntityMove RelativeEntityMove;
RelativeEntityMove.m_UniqueID = GetUniqueID();
RelativeEntityMove.m_MoveX = (char)(DiffX*32);
RelativeEntityMove.m_MoveY = (char)(DiffY*32);
RelativeEntityMove.m_MoveZ = (char)(DiffZ*32);
InChunk->Broadcast( RelativeEntityMove, m_ClientHandle );
}
}
m_LastPosX = GetPosX();
m_LastPosY = GetPosY();
m_LastPosZ = GetPosZ();
m_bDirtyPosition = false;
m_ClientHandle->StreamChunks();
}
if( m_Health > 0 ) // make sure player is alive
{
if( cWorld::GetTime() - m_TimeLastPickupCheck > 0.5f ) // Each 0.5 second, check for pickups
{
m_TimeLastPickupCheck = cWorld::GetTime();
// and also check if near a pickup
// TODO: Don't only check in current chunks, but also close chunks (chunks within range)
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
Chunk->LockEntities();
cWorld::EntityList Entities = Chunk->GetEntities();
for( cWorld::EntityList::iterator itr = Entities.begin(); itr != Entities.end();++itr)
{
if( (*itr)->GetEntityType() != cEntity::E_PICKUP ) continue; // Only pickups
float DiffX = (float)((*itr)->GetPosX() - GetPosX() );
float DiffY = (float)((*itr)->GetPosY() - GetPosY() );
float DiffZ = (float)((*itr)->GetPosZ() - GetPosZ() );
float SqrDist = DiffX*DiffX + DiffY*DiffY + DiffZ*DiffZ;
if(SqrDist < 1.5f*1.5f) // 1.5 block
{
cPickup* Pickup = reinterpret_cast<cPickup*>(*itr);
Pickup->CollectedBy( this );
}
}
Chunk->UnlockEntities();
}
}
CheckMetaDataBurn();
if(e_EPMetaState == BURNING){
InStateBurning(a_Dt);
}
}
void cPlayer::InStateBurning(float a_Dt) {
m_FireDamageInterval += a_Dt;
char block = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z );
char bblock = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y -1, (int)m_Pos->z );
if(m_FireDamageInterval > 1000) {
m_FireDamageInterval = 0;
int rem = rand()%3 + 1; //Burn most of the time
if(rem >= 2) {
//printf("OUCH burning!!!\n");
TakeDamage(1, this);
}
m_BurnPeriod++;
if(block == E_BLOCK_LAVA || block == E_BLOCK_STATIONARY_LAVA || block == E_BLOCK_FIRE
|| bblock == E_BLOCK_LAVA || bblock == E_BLOCK_STATIONARY_LAVA || bblock == E_BLOCK_FIRE)
m_BurnPeriod = 0;
if(m_BurnPeriod > 5) {
cChunk* InChunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
e_EPMetaState = NORMAL;
cPacket_Metadata md(NORMAL, GetUniqueID());
//md.m_UniqueID = GetUniqueID();
InChunk->Broadcast(md);
m_BurnPeriod = 0;
}
}
}
//----Change Entity MetaData
void cPlayer::CheckMetaDataBurn() {
char block = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z );
char bblock = cRoot::Get()->GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y -1, (int)m_Pos->z );
if(m_bBurnable && e_EPMetaState != BURNING && (block == E_BLOCK_LAVA || block == E_BLOCK_STATIONARY_LAVA || block == E_BLOCK_FIRE
|| bblock == E_BLOCK_LAVA || bblock == E_BLOCK_STATIONARY_LAVA || bblock == E_BLOCK_FIRE)) {
cChunk* InChunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if(!InChunk)
return;
printf("I should burn");
e_EPMetaState = BURNING;
cPacket_Metadata md(BURNING,GetUniqueID());
InChunk->Broadcast(md);
}
}
void cPlayer::SetTouchGround( bool a_bTouchGround )
{
m_bTouchGround = a_bTouchGround;
if( !m_bTouchGround )
{
cWorld* World = cRoot::Get()->GetWorld();
char BlockID = World->GetBlock( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z );
if( BlockID != E_BLOCK_AIR )
{
m_bTouchGround = true;
}
if( BlockID == E_BLOCK_WATER || BlockID == E_BLOCK_STATIONARY_WATER || BlockID == E_BLOCK_LADDER || BlockID == E_BLOCK_TORCH )
{
m_LastGroundHeight = (float)m_Pos->y;
}
}
if( m_bTouchGround )
{
float Dist = (float)(m_LastGroundHeight - m_Pos->y);
if( Dist > 4.f ) // Player dropped
{
int Damage = (int)(Dist - 4.f);
if( Damage > 0 )
{
TakeDamage( Damage, 0 );
}
}
m_LastGroundHeight = (float)m_Pos->y;
}
}
void cPlayer::Heal( int a_Health )
{
if( m_Health < 20 )
{
m_Health += (short)a_Health;
if( m_Health > 20 )
{
m_Health = 20;
}
cPacket_UpdateHealth Health;
Health.m_Health = m_Health;
m_ClientHandle->Send( Health );
}
}
void cPlayer::TakeDamage( int a_Damage, cEntity* a_Instigator )
{
cPawn::TakeDamage( a_Damage, a_Instigator );
cPacket_UpdateHealth Health;
Health.m_Health = m_Health;
m_ClientHandle->Send( Health );
}
void cPlayer::KilledBy( cEntity* a_Killer )
{
cPawn::KilledBy( a_Killer );
if( m_Health > 0 ) return; // not dead yet =]
m_bVisible = false; // So new clients don't see the player
// Puke out all the items
cItem* Items = m_Inventory->GetSlots();
for( unsigned int i = 1; i < m_Inventory->c_NumSlots; ++i )
{
if( !Items[i].IsEmpty() )
{
float SpeedX = ((rand()%1000)-500) /100.f;
float SpeedY = ((rand()%1000)) /100.f;
float SpeedZ = ((rand()%1000)-500) /100.f;
cPickup* Pickup = new cPickup( (int)(m_Pos->x*32), (int)(m_Pos->y*32), (int)(m_Pos->z*32), Items[i], SpeedX, SpeedY, SpeedZ );
Pickup->Initialize();
}
Items[i].Empty();
}
SaveToDisk(); // Save it, yeah the world is a tough place !
}
void cPlayer::Respawn()
{
m_Health = 20;
cWorld* World = cRoot::Get()->GetWorld();
m_ClientHandle->Send( cPacket_Respawn() );
TeleportTo( World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ() );
SetVisible( true );
}
double cPlayer::GetEyeHeight()
{
return m_Stance;
}
Vector3d cPlayer::GetEyePosition()
{
return Vector3d( m_Pos->x, m_Stance, m_Pos->z );
}
void cPlayer::OpenWindow( cWindow* a_Window )
{
CloseWindow();
a_Window->Open( *this );
m_CurrentWindow = a_Window;
}
void cPlayer::CloseWindow()
{
if( m_CurrentWindow ) m_CurrentWindow->Close( *this );
m_CurrentWindow = 0;
}
#ifdef SendMessage // Cause stupid windows.h defines SendMessage as SendMessageA
#undef SendMessage
#endif
void cPlayer::SendMessage( const char* a_Message )
{
m_ClientHandle->Send( cPacket_Chat( a_Message ) );
}
void cPlayer::TeleportTo( cEntity* a_Entity )
{
cPawn::TeleportTo( a_Entity );
cPacket_PlayerPosition PlayerPosition( this );
m_ClientHandle->Send( PlayerPosition );
}
void cPlayer::TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ )
{
cPawn::TeleportTo( a_PosX, a_PosY, a_PosZ );
cPacket_PlayerPosition PlayerPosition( this );
m_ClientHandle->Send( PlayerPosition );
}
void cPlayer::MoveTo( const Vector3d & a_NewPos )
{
// TODO: should do some checks to see if player is not moving through terrain
SetPosition( a_NewPos );
}
void cPlayer::SetVisible( bool a_bVisible )
{
if( a_bVisible == true && m_bVisible == false ) // Make visible
{
m_bVisible = true;
SpawnOn( 0 ); // Spawn on everybody
}
if( a_bVisible == false && m_bVisible == true )
{
m_bVisible = false;
cPacket_DestroyEntity DestroyEntity( this );
cChunk* Chunk = cRoot::Get()->GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ );
if( Chunk )
{
Chunk->Broadcast( DestroyEntity ); // Destroy on all clients
}
}
}
void cPlayer::AddToGroup( const char* a_GroupName )
{
cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName );
m_pState->Groups.push_back( Group );
LOG("Added %s to group %s", m_pState->PlayerName.c_str(), a_GroupName );
ResolveGroups();
ResolvePermissions();
}
bool cPlayer::CanUseCommand( const char* a_Command )
{
for( GroupList::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr )
{
if( (*itr)->HasCommand( a_Command ) ) return true;
}
return false;
}
bool cPlayer::HasPermission( const char* a_Permission )
{
std::vector< std::string > Split = StringSplit( a_Permission, "." );
PermissionMap Possibilities = m_pState->ResolvedPermissions;
// Now search the namespaces
while( Possibilities.begin() != Possibilities.end() )
{
PermissionMap::iterator itr = Possibilities.begin();
if( itr->second )
{
std::vector< std::string > OtherSplit = StringSplit( itr->first, "." );
if( OtherSplit.size() <= Split.size() )
{
unsigned int i;
for( i = 0; i < OtherSplit.size(); ++i )
{
if( OtherSplit[i].compare( Split[i] ) != 0 )
{
if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard!
break;
}
}
if( i == Split.size() ) return true;
}
}
Possibilities.erase( itr );
}
// Nothing that matched :(
return false;
}
bool cPlayer::IsInGroup( const char* a_Group )
{
for( GroupList::iterator itr = m_pState->ResolvedGroups.begin(); itr != m_pState->ResolvedGroups.end(); ++itr )
{
if( strcmp( a_Group, (*itr)->GetName().c_str() ) == 0 )
return true;
}
return false;
}
void cPlayer::ResolvePermissions()
{
m_pState->ResolvedPermissions.clear(); // Start with an empty map yo~
// Copy all player specific permissions into the resolved permissions map
for( PermissionMap::iterator itr = m_pState->Permissions.begin(); itr != m_pState->Permissions.end(); ++itr )
{
m_pState->ResolvedPermissions[ itr->first ] = itr->second;
}
for( GroupList::iterator GroupItr = m_pState->ResolvedGroups.begin(); GroupItr != m_pState->ResolvedGroups.end(); ++GroupItr )
{
const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions();
for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr )
{
m_pState->ResolvedPermissions[ itr->first ] = itr->second;
}
}
}
void cPlayer::ResolveGroups()
{
// Clear resolved groups first
m_pState->ResolvedGroups.clear();
// Get a complete resolved list of all groups the player is in
std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates
GroupList ToIterate;
for( GroupList::iterator GroupItr = m_pState->Groups.begin(); GroupItr != m_pState->Groups.end(); ++GroupItr )
{
ToIterate.push_back( *GroupItr );
}
while( ToIterate.begin() != ToIterate.end() )
{
cGroup* CurrentGroup = *ToIterate.begin();
if( AllGroups.find( CurrentGroup ) != AllGroups.end() )
{
LOGERROR("ERROR: Player %s is in the same group multiple times (%s). FIX IT!", m_pState->PlayerName.c_str(), CurrentGroup->GetName().c_str() );
}
else
{
AllGroups[ CurrentGroup ] = true;
m_pState->ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list
const cGroup::GroupList & Inherits = CurrentGroup->GetInherits();
for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr )
{
if( AllGroups.find( *itr ) != AllGroups.end() )
{
LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_pState->PlayerName.c_str(), (*itr)->GetName().c_str() );
continue;
}
ToIterate.push_back( *itr );
}
}
ToIterate.erase( ToIterate.begin() );
}
}
std::string cPlayer::GetColor()
{
if( m_Color != '-' )
return cChatColor::MakeColor( m_Color );
if( m_pState->Groups.size() < 1 )
return cChatColor::White;
return (*m_pState->Groups.begin())->GetColor();
}
void cPlayer::TossItem( bool a_bDraggingItem, int a_Amount /* = 1 */ )
{
if( a_bDraggingItem )
{
cItem* Item = GetInventory().GetWindow()->GetDraggingItem();
if( Item->m_ItemID > 0 && Item->m_ItemCount >= a_Amount )
{
float vX = 0, vY = 0, vZ = 0;
EulerToVector( -GetRotation(), GetPitch(), vZ, vX, vY );
vY = -vY*2 + 1.f;
cPickup* Pickup = new cPickup( (int)(GetPosX()*32), (int)(GetPosY()*32) + (int)(1.6f*32), (int)(GetPosZ()*32), cItem( Item->m_ItemID, (char)a_Amount, Item->m_ItemHealth), vX*2, vY*2, vZ*2 );
Pickup->Initialize();
if( Item->m_ItemCount > a_Amount )
Item->m_ItemCount -= (char)a_Amount;
else
Item->Empty();
}
return;
}
// Else drop equipped item
cItem DroppedItem = GetInventory().GetEquippedItem();
if( DroppedItem.m_ItemID > 0 && DroppedItem.m_ItemCount > 0 )
{
DroppedItem.m_ItemCount = 1;
if( GetInventory().RemoveItem( DroppedItem ) )
{
DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again
float vX = 0, vY = 0, vZ = 0;
EulerToVector( -GetRotation(), GetPitch(), vZ, vX, vY );
vY = -vY*2 + 1.f;
cPickup* Pickup = new cPickup( (int)(GetPosX()*32), (int)(GetPosY()*32) + (int)(1.6f*32), (int)(GetPosZ()*32), DroppedItem, vX*2, vY*2, vZ*2 );
Pickup->Initialize();
}
}
}
bool cPlayer::LoadFromDisk()
{
cIniFile IniFile("users.ini");
if( IniFile.ReadFile() )
{
std::string Groups = IniFile.GetValue(m_pState->PlayerName, "Groups", "");
if( Groups.size() > 0 )
{
std::vector< std::string > Split = StringSplit( Groups, "," );
for( unsigned int i = 0; i < Split.size(); i++ )
{
AddToGroup( Split[i].c_str() );
}
}
else
{
AddToGroup("Default");
}
m_Color = IniFile.GetValue(m_pState->PlayerName, "Color", "-")[0];
}
else
{
AddToGroup("Default");
}
ResolvePermissions();
// Log player permissions, cause it's what the cool kids do
LOGINFO("Player %s has permissions:", m_pState->PlayerName.c_str() );
for( PermissionMap::iterator itr = m_pState->ResolvedPermissions.begin(); itr != m_pState->ResolvedPermissions.end(); ++itr )
{
if( itr->second ) LOGINFO("%s", itr->first.c_str() );
}
char SourceFile[128];
sprintf_s(SourceFile, 128, "world/player/%s.bin", m_pState->PlayerName.c_str() );
FILE* f;
#ifdef _WIN32
if( fopen_s(&f, SourceFile, "rb" ) == 0 ) // no error
#else
if( (f = fopen(SourceFile, "rb" ) ) != 0 ) // no error
#endif
{
if( fread( &m_Pos->x, sizeof(double), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &m_Pos->y, sizeof(double), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &m_Pos->z, sizeof(double), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &m_Rot->x, sizeof(float), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &m_Rot->y, sizeof(float), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &m_Rot->z, sizeof(float), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( fread( &m_Health, sizeof(m_Health), 1, f) != 1 ) { LOGERROR("ERROR READING FROM FILE %s", SourceFile); fclose(f); return false; }
if( !m_Inventory->LoadFromFile( f ) ) { LOGERROR("ERROR READING INVENTORY FROM FILE %s", SourceFile); fclose(f); return false; }
fclose(f);
return true;
}
return false;
}
bool cPlayer::SaveToDisk()
{
#ifdef _WIN32
{ // Make sure some folders exist
SECURITY_ATTRIBUTES Attrib;
Attrib.nLength = sizeof(SECURITY_ATTRIBUTES);
Attrib.lpSecurityDescriptor = NULL;
Attrib.bInheritHandle = false;
::CreateDirectory("world", &Attrib);
::CreateDirectory("world/player", &Attrib);
}
#else
{
mkdir("world", S_IRWXU | S_IRWXG | S_IRWXO);
mkdir("world/player", S_IRWXU | S_IRWXG | S_IRWXO);
}
#endif
char SourceFile[128];
sprintf_s(SourceFile, 128, "world/player/%s.bin", m_pState->PlayerName.c_str() );
FILE* f;
#ifdef _WIN32
if( fopen_s(&f, SourceFile, "wb" ) == 0 ) // no error
#else
if( (f = fopen(SourceFile, "wb" ) ) != 0 ) // no error
#endif
{
fwrite( &m_Pos->x, sizeof(double), 1, f );
fwrite( &m_Pos->y, sizeof(double), 1, f );
fwrite( &m_Pos->z, sizeof(double), 1, f );
fwrite( &m_Rot->x, sizeof(float), 1, f );
fwrite( &m_Rot->y, sizeof(float), 1, f );
fwrite( &m_Rot->z, sizeof(float), 1, f );
fwrite( &m_Health, sizeof(m_Health), 1, f );
m_Inventory->WriteToFile( f );
fclose(f);
return true;
}
LOGERROR("ERROR WRITING PLAYER %s TO FILE %s", m_pState->PlayerName.c_str(), SourceFile);
return false;
}
const char* cPlayer::GetName()
{
return m_pState->PlayerName.c_str();
}
void cPlayer::SetName( const char* a_Name )
{
m_pState->PlayerName = a_Name;
}
const cPlayer::GroupList & cPlayer::GetGroups()
{
return m_pState->Groups;
}

96
source/cPlayer.h Normal file
View File

@ -0,0 +1,96 @@
#pragma once
#include "cPawn.h"
#include <list>
class cGroup;
class cWindow;
class cInventory;
class cClientHandle;
class cPlayer : public cPawn //tolua_export
{ //tolua_export
public:
CLASS_PROTOTYPE();
cPlayer(cClientHandle* a_Client, const char* a_PlayerName);
virtual ~cPlayer();
virtual void SpawnOn( cClientHandle* a_Target );
virtual void Tick(float a_Dt);
void SetTouchGround( bool a_bTouchGround );
inline void SetStance( const double & a_Stance ) { m_Stance = a_Stance; }
double GetEyeHeight(); //tolua_export
Vector3d GetEyePosition(); //tolua_export
inline bool GetFlying() { return m_bTouchGround; } //tolua_export
inline const double & GetStance() { return m_Stance; } //tolua_export
cInventory & GetInventory() { return *m_Inventory; } //tolua_export
virtual void TeleportTo( cEntity* a_Entity ); //tolua_export
virtual void TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export
// Tries to move to a new position, with collision checks and stuff
virtual void MoveTo( const Vector3d & a_NewPos ); //tolua_export
cWindow* GetWindow() { return m_CurrentWindow; }
void OpenWindow( cWindow* a_Window );
void CloseWindow();
cClientHandle* GetClientHandle() { return m_ClientHandle; } //tolua_export
void SetClientHandle( cClientHandle* a_Client ) { m_ClientHandle = a_Client; }
void SendMessage( const char* a_Message ); //tolua_export
const char* GetName(); //tolua_export
void SetName( const char* a_Name ); //tolua_export
typedef std::list< cGroup* > GroupList;
void AddToGroup( const char* a_GroupName ); //tolua_export
bool CanUseCommand( const char* a_Command ); //tolua_export
bool HasPermission( const char* a_Permission ); //tolua_export
const GroupList & GetGroups(); // >> EXPORTED IN MANUALBINDINGS <<
bool IsInGroup( const char* a_Group ); //tolua_export
std::string GetColor(); //tolua_export
void TossItem( bool a_bDraggingItem, int a_Amount = 1 ); //tolua_export
void Heal( int a_Health ); //tolua_export
void TakeDamage( int a_Damage, cEntity* a_Instigator ); //tolua_export
void KilledBy( cEntity* a_Killer ); //tolua_export
void Respawn(); //tolua_export
void SetVisible( bool a_bVisible ); //tolua_export
bool IsVisible() { return m_bVisible; } //tolua_export
bool SaveToDisk();
bool LoadFromDisk();
//Burning logic
bool m_bBurnable;
enum PMetaState{NORMAL,BURNING,CROUCHED,RIDING} e_EPMetaState;
virtual void CheckMetaDataBurn();
virtual void InStateBurning(float a_Dt);
protected:
struct sPlayerState;
sPlayerState* m_pState;
bool m_bVisible;
float m_LastGroundHeight;
bool m_bTouchGround;
double m_Stance;
cInventory* m_Inventory;
cWindow* m_CurrentWindow;
float m_TimeLastPickupCheck;
void ResolvePermissions();
void ResolveGroups();
char m_Color;
float m_FireDamageInterval;
float m_BurnPeriod;
cClientHandle* m_ClientHandle;
}; //tolua_export

78
source/cPlugin.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "cPlugin.h"
#include <stdio.h>
#include "cMCLogger.h"
cPlugin::cPlugin()
: m_Version( 0 )
{
}
cPlugin::~cPlugin()
{
}
// bool cPlugin::Initialize()
// {
// LOG("cPlugin::Initialize()");
// return false;
// }
void cPlugin::Tick(float a_Dt)
{
(void)a_Dt;
}
bool cPlugin::OnBlockPlace( cPacket_BlockPlace* a_PacketData, cPlayer* a_Player )
{
(void)a_PacketData;
(void)a_Player;
return false;
}
bool cPlugin::OnCollectItem( cPickup* a_Pickup, cPlayer* a_Player )
{
(void)a_Pickup;
(void)a_Player;
return false;
}
bool cPlugin::OnDisconnect( std::string a_Reason, cPlayer* a_Player )
{
(void)a_Reason;
(void)a_Player;
return false;
}
bool cPlugin::OnChat( std::string a_Chat, cPlayer* a_Player )
{
(void)a_Chat;
(void)a_Player;
return false;
}
bool cPlugin::OnLogin( cPacket_Login* a_PacketData )
{
(void)a_PacketData;
return false;
}
void cPlugin::OnPlayerSpawn( cPlayer* a_Player )
{
(void)a_Player;
}
bool cPlugin::OnPlayerJoin( cPlayer* a_Player )
{
(void)a_Player;
return false;
}
void cPlugin::AddCommand( std::string & a_Command, std::string & a_Description, std::string & a_Permission )
{
CommandStruct Command;
Command.Command = a_Command;
Command.Description = a_Description;
Command.Permission = a_Permission;
m_Commands.push_back( Command );
}

76
source/cPlugin.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include "MemoryLeak.h"
#include <vector>
#include <string>
class cPacket_BlockPlace;
class cPacket_PickupSpawn;
class cPacket_EntityEquipment;
class cPacket_Disconnect;
class cPacket_Chat;
class cPacket_BlockDig;
class cPacket_Login;
class cClientHandle;
class cPlayer;
class cPickup;
class cItem;
class cEntity;
class cPawn;
struct TakeDamageInfo;
// tolua_begin
class cPlugin
{
public:
cPlugin();
virtual ~cPlugin();
virtual void OnDisable() {}
virtual bool Initialize() = 0;
// Called each tick
virtual void Tick(float a_Dt);
/**
* On all these functions, return true if you want to override default behavior
* You can also return false, so default behavior is used, but with changed PacketData
**/
virtual bool OnCollectItem( cPickup* a_Pickup, cPlayer* a_Player );
virtual bool OnDisconnect( std::string a_Reason, cPlayer* a_Player );
virtual bool OnBlockPlace( cPacket_BlockPlace* a_PacketData, cPlayer* a_Player );
virtual bool OnBlockDig( cPacket_BlockDig* a_PacketData, cPlayer* a_Player, cItem* a_PickupItem ) { (void)a_PacketData; (void)a_Player; (void)a_PickupItem; return false; }
virtual bool OnChat( std::string a_Chat, cPlayer* a_Player );
virtual bool OnLogin( cPacket_Login* a_PacketData );
virtual void OnPlayerSpawn( cPlayer* a_Player );
virtual bool OnPlayerJoin( cPlayer* a_Player );
virtual void OnPlayerMove( cPlayer* a_Player ) { (void)a_Player; }
virtual void OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) { (void)a_Pawn; (void)a_TakeDamageInfo; }
virtual bool OnKilled( cPawn* a_Killed, cEntity* a_Killer ) { (void)a_Killed; (void)a_Killer; return false; }
// Accessors
std::string GetName() const { return m_Name; }
void SetName( std::string a_Name ) { m_Name = a_Name; }
int GetVersion() const { return m_Version; }
void SetVersion( int a_Version ) { m_Version = a_Version; }
struct CommandStruct
{
std::string Command;
std::string Description;
std::string Permission;
};
void AddCommand( std::string & a_Command, std::string & a_Description, std::string & a_Permission );
// tolua_end
typedef bool (FuncCommandHandler)( std::string & a_Command, std::vector< std::string > & a_Split );
void BindCommand( FuncCommandHandler* a_Function, std::string & a_Command ); // >> EXPORTED IN MANUALBINDINGS <<
const std::vector< CommandStruct > & GetCommands() const { return m_Commands; } // >> EXPORTED IN MANUALBINDINGS <<
private:
std::vector< CommandStruct > m_Commands;
std::string m_Name;
int m_Version;
}; //tolua_export

423
source/cPluginManager.cpp Normal file
View File

@ -0,0 +1,423 @@
#include "cPluginManager.h"
#include "cPlugin.h"
#include "cPlugin_Lua.h"
#include "cMCLogger.h"
#include "cWebAdmin.h"
#include "cItem.h"
#include "cRoot.h"
#include "cLuaCommandBinder.h"
#include <stdarg.h>
#include "../iniFile/iniFile.h"
extern std::vector<std::string> StringSplit(std::string str, std::string delim);
typedef std::list< cPlugin_Lua* > LuaPluginList;
typedef std::map< cPluginManager::PluginHook, cPluginManager::PluginList > HookMap;
struct cPluginManager::sPluginManagerState
{
LuaPluginList LuaPlugins;
cPluginManager::PluginList Plugins;
HookMap Hooks;
};
cPluginManager* cPluginManager::GetPluginManager()
{
LOGWARN("WARNING: Using deprecated function cPluginManager::GetPluginManager() use cRoot::Get()->GetPluginManager() instead!");
return cRoot::Get()->GetPluginManager();
}
cPluginManager::cPluginManager()
: m_pState( new sPluginManagerState )
, m_LuaCommandBinder( new cLuaCommandBinder() )
, m_bReloadPlugins(false)
{
}
cPluginManager::~cPluginManager()
{
UnloadPluginsNow();
delete m_LuaCommandBinder;
delete m_pState;
}
void cPluginManager::ReloadPlugins()
{
m_bReloadPlugins = true;
}
void cPluginManager::ReloadPluginsNow()
{
LOG("--Loading plugins--");
m_bReloadPlugins = false;
UnloadPluginsNow();
cIniFile IniFile("settings.ini");
if( IniFile.ReadFile() )
{
unsigned int KeyNum = IniFile.FindKey("Plugins");
unsigned int NumPlugins = IniFile.GetNumValues( KeyNum );
if( NumPlugins > 0 )
{
for(unsigned int i = 0; i < NumPlugins; i++)
{
std::string ValueName = IniFile.GetValueName(KeyNum, i );
if( ValueName.compare("Plugin") == 0 )
{ // It's a plugin
std::string PluginFile = IniFile.GetValue(KeyNum, i );
if( PluginFile.compare("") != 0 )
{
// allow for comma separated plugin list
// degrades and works fine for the plugin
// per line
std::vector< std::string > split = StringSplit( PluginFile, "," );
for (unsigned int j = 0; j < split.size(); j++) {
cPlugin_Lua* Plugin = new cPlugin_Lua( (split[j] + std::string(".lua") ).c_str() );
if( !AddLuaPlugin( Plugin ) )
{
delete Plugin;
}
}
}
}
}
}
if( GetNumPlugins() == 0 )
{
LOG("No plugins loaded");
}
else
{
LOG("Loaded %i plugin(s)", GetNumPlugins() );
}
}
else
{
LOG("WARNING: Can't find settings.ini, so can't load any plugins.");
}
LOG("--Done loading plugins--");
}
void cPluginManager::Tick(float a_Dt)
{
if( m_bReloadPlugins )
{
ReloadPluginsNow();
}
HookMap::iterator Plugins = m_pState->Hooks.find( E_PLUGIN_TICK );
if( Plugins != m_pState->Hooks.end() )
{
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
(*itr)->Tick( a_Dt );
}
}
}
bool cPluginManager::CallHook( PluginHook a_Hook, unsigned int a_NumArgs, ... )
{
HookMap::iterator Plugins = m_pState->Hooks.find( a_Hook );
// Special case for chat hook, since you can also bind commands (bound commands don't use chat hook)
if( a_Hook == E_PLUGIN_CHAT )
{
if( a_NumArgs != 2 ) return false;
va_list argptr;
va_start( argptr, a_NumArgs);
const char* Message = va_arg(argptr, const char* );
cPlayer* Player = va_arg(argptr, cPlayer* );
va_end (argptr);
if( m_LuaCommandBinder->HandleCommand( std::string( Message ), Player ) )
return true;
if( Plugins != m_pState->Hooks.end() )
{
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnChat( Message, Player ) )
return true;
}
}
return false;
}
if( Plugins != m_pState->Hooks.end() )
{
switch( a_Hook )
{
case E_PLUGIN_COLLECT_ITEM:
{
if( a_NumArgs != 2 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPickup* Pickup = va_arg(argptr, cPickup* );
cPlayer* Player = va_arg(argptr, cPlayer* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnCollectItem( Pickup, Player ) )
return true;
}
}
break;
case E_PLUGIN_BLOCK_DIG:
{
if( a_NumArgs != 2 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPacket_BlockDig* Packet = va_arg(argptr, cPacket_BlockDig* );
cPlayer* Player = va_arg(argptr, cPlayer* );
cItem* Item = va_arg( argptr, cItem* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnBlockDig( Packet, Player, Item ) )
return true;
}
}
break;
case E_PLUGIN_BLOCK_PLACE:
{
if( a_NumArgs != 2 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPacket_BlockPlace* Packet = va_arg(argptr, cPacket_BlockPlace* );
cPlayer* Player = va_arg(argptr, cPlayer* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnBlockPlace( Packet, Player ) )
return true;
}
}
break;
case E_PLUGIN_DISCONNECT:
{
if( a_NumArgs != 2 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
const char* Reason = va_arg(argptr, const char* );
cPlayer* Player = va_arg(argptr, cPlayer* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnDisconnect( Reason, Player ) )
return true;
}
}
break;
case E_PLUGIN_LOGIN:
{
if( a_NumArgs != 1 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPacket_Login* Packet = va_arg(argptr, cPacket_Login* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnLogin( Packet ) )
return true;
}
}
break;
case E_PLUGIN_PLAYER_JOIN:
{
if( a_NumArgs != 1 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPlayer* Player = va_arg(argptr, cPlayer* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnPlayerJoin( Player ) )
return true;
}
}
break;
case E_PLUGIN_PLAYER_MOVE:
{
if( a_NumArgs != 1 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPlayer* Player = va_arg(argptr, cPlayer* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
(*itr)->OnPlayerMove( Player );
}
}
break;
case E_PLUGIN_TAKE_DAMAGE:
{
if( a_NumArgs != 2 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPawn* Pawn = va_arg(argptr, cPawn* );
TakeDamageInfo* TDI = va_arg(argptr, TakeDamageInfo* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
(*itr)->OnTakeDamage( Pawn, TDI );
}
}
break;
case E_PLUGIN_KILLED:
{
if( a_NumArgs != 2 ) break;
va_list argptr;
va_start( argptr, a_NumArgs);
cPawn* Killed = va_arg(argptr, cPawn* );
cEntity* Killer = va_arg(argptr, cEntity* );
va_end (argptr);
for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr )
{
if( (*itr)->OnKilled( Killed, Killer ) )
return true;
}
}
break;
default:
LOG("WARNING: Calling Unknown hook: %i", a_Hook );
break;
}
}
return false;
}
cPlugin* cPluginManager::GetPlugin( std::string a_Plugin )
{
for( PluginList::iterator itr = m_pState->Plugins.begin(); itr != m_pState->Plugins.end(); ++itr )
{
if( (*itr)->GetName().compare( a_Plugin ) == 0 )
{
return *itr;
}
}
return 0;
}
const cPluginManager::PluginList & cPluginManager::GetAllPlugins()
{
return m_pState->Plugins;
}
void cPluginManager::UnloadPluginsNow()
{
m_pState->Hooks.clear();
while( m_pState->LuaPlugins.size() > 0 )
{
cPlugin_Lua* LuaPlugin = *m_pState->LuaPlugins.begin();
if( LuaPlugin )
{
cWebAdmin* WebAdmin = cRoot::Get()->GetWebAdmin();
if( WebAdmin ) WebAdmin->RemovePlugin( LuaPlugin->GetLuaState() );
delete LuaPlugin;
}
m_pState->LuaPlugins.remove( LuaPlugin );
}
while( m_pState->Plugins.size() > 0 )
{
RemovePlugin( *m_pState->Plugins.begin(), true );
}
}
void cPluginManager::RemovePlugin( cPlugin* a_Plugin, bool a_bDelete /* = false */ )
{
if( a_bDelete )
{
m_LuaCommandBinder->RemoveBindingsForPlugin( a_Plugin );
m_pState->Plugins.remove( a_Plugin );
a_Plugin->OnDisable();
delete a_Plugin;
}
else
{
for( LuaPluginList::iterator itr = m_pState->LuaPlugins.begin(); itr != m_pState->LuaPlugins.end(); ++itr )
{
(*itr)->RemovePlugin( a_Plugin );
}
}
}
bool cPluginManager::AddPlugin( cPlugin* a_Plugin )
{
if( a_Plugin->Initialize() )
{
m_pState->Plugins.remove( a_Plugin );
m_pState->Plugins.push_back( a_Plugin );
return true;
}
return false;
}
bool cPluginManager::AddPlugin( lua_State* a_LuaState, cPlugin* a_Plugin )
{
cPlugin_Lua* LuaPlugin = GetLuaPlugin( a_LuaState );
if( LuaPlugin && a_Plugin->Initialize() )
{
m_pState->Plugins.remove( a_Plugin );
m_pState->Plugins.push_back( a_Plugin );
LuaPlugin->AddPlugin( a_Plugin );
return true;
}
return false;
}
bool cPluginManager::AddLuaPlugin( cPlugin_Lua* a_Plugin )
{
m_pState->LuaPlugins.push_back( a_Plugin ); // It HAS to be in here before calling Initialize, so it can be found by AddPlugin()
if(a_Plugin->Initialize() )
{
return true;
}
LOG(">>>>>>> Could not initialize a plugin! ");
m_pState->LuaPlugins.remove( a_Plugin );
return false;
}
void cPluginManager::RemoveLuaPlugin( std::string a_FileName )
{
for( LuaPluginList::iterator itr = m_pState->LuaPlugins.begin(); itr != m_pState->LuaPlugins.end(); ++itr )
{
if( (*itr)->GetFileName() == a_FileName )
{
cPlugin_Lua* Plugin = *itr;
delete Plugin;
m_pState->LuaPlugins.remove( Plugin );
return;
}
}
}
cPlugin_Lua* cPluginManager::GetLuaPlugin( lua_State* a_State )
{
for( LuaPluginList::iterator itr = m_pState->LuaPlugins.begin(); itr != m_pState->LuaPlugins.end(); ++itr )
{
if( (*itr)->GetLuaState() == a_State )
{
return *itr;
}
}
return 0;
}
void cPluginManager::AddHook( cPlugin* a_Plugin, PluginHook a_Hook )
{
PluginList & Plugins = m_pState->Hooks[ a_Hook ];
Plugins.remove( a_Plugin );
Plugins.push_back( a_Plugin );
}
unsigned int cPluginManager::GetNumPlugins()
{
return m_pState->Plugins.size();
}

68
source/cPluginManager.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include <list>
struct lua_State;
class cLuaCommandBinder;
class cPlugin;
class cPlugin_Lua;
class cPluginManager //tolua_export
{ //tolua_export
public: //tolua_export
// Called each tick
virtual void Tick(float a_Dt);
enum PluginHook //tolua_export
{ //tolua_export
E_PLUGIN_TICK, //tolua_export
E_PLUGIN_CHAT, //tolua_export
E_PLUGIN_COLLECT_ITEM, //tolua_export
E_PLUGIN_BLOCK_DIG, //tolua_export
E_PLUGIN_BLOCK_PLACE, //tolua_export
E_PLUGIN_DISCONNECT, //tolua_export
E_PLUGIN_HANDSHAKE, //tolua_export
E_PLUGIN_LOGIN, //tolua_export
E_PLUGIN_PLAYER_SPAWN, //tolua_export
E_PLUGIN_PLAYER_JOIN, //tolua_export
E_PLUGIN_PLAYER_MOVE, //tolua_export
E_PLUGIN_TAKE_DAMAGE, //tolua_export
E_PLUGIN_KILLED, //tolua_export
}; //tolua_export
static cPluginManager * GetPluginManager(); //tolua_export
typedef std::list< cPlugin* > PluginList;
cPlugin* GetPlugin( std::string a_Plugin ); //tolua_export
const PluginList & GetAllPlugins(); // >> EXPORTED IN MANUALBINDINGS <<
void ReloadPlugins(); //tolua_export
bool AddPlugin( cPlugin* a_Plugin );
bool AddPlugin( lua_State* a_LuaState, cPlugin* a_Plugin ); //tolua_export
bool AddLuaPlugin( cPlugin_Lua* a_Plugin );
void AddHook( cPlugin* a_Plugin, PluginHook a_Hook ); //tolua_export
unsigned int GetNumPlugins(); //tolua_export
bool CallHook( PluginHook a_Hook, unsigned int a_NumArgs, ... );
void RemovePlugin( cPlugin* a_Plugin, bool a_bDelete = false ); //tolua_export
void RemoveLuaPlugin( std::string a_FileName ); //tolua_export
cPlugin_Lua* GetLuaPlugin( lua_State* a_State ); //tolua_export
cLuaCommandBinder* GetLuaCommandBinder() { return m_LuaCommandBinder; }
private:
friend class cRoot;
cPluginManager();
~cPluginManager();
struct sPluginManagerState;
sPluginManagerState* m_pState;
void ReloadPluginsNow();
void UnloadPluginsNow();
cLuaCommandBinder* m_LuaCommandBinder;
bool m_bReloadPlugins;
}; //tolua_export

97
source/cPlugin_Lua.cpp Normal file
View File

@ -0,0 +1,97 @@
#define LUA_USE_POSIX
#include "cMCLogger.h"
#include <string>
#include "cPlugin_Lua.h"
#include "cPluginManager.h"
#include "cRoot.h"
extern "C"
{
#include "lualib.h"
}
#include "tolua++.h"
#include "Bindings.h"
#include "ManualBindings.h"
bool report_errors(lua_State* lua, int status)
{
if ( status!=0 )
{
std::string s = lua_tostring(lua, -1);
LOGERROR("-- %s", s.c_str() );
lua_pop(lua, 1);
return true;
}
return false;
}
cPlugin_Lua::~cPlugin_Lua()
{
UnloadPlugins();
if( m_LuaState )
{
lua_close( m_LuaState );
m_LuaState = 0;
}
}
cPlugin_Lua::cPlugin_Lua(const char* a_Plugin)
: m_LuaState( 0 )
{
m_FileName.assign( a_Plugin );
}
bool cPlugin_Lua::Initialize()
{
if( !m_LuaState )
{
m_LuaState = lua_open();
luaL_openlibs( m_LuaState );
tolua_AllToLua_open(m_LuaState);
ManualBindings::Bind( m_LuaState );
}
int s = luaL_loadfile(m_LuaState, (std::string("Plugins/") + m_FileName ).c_str() );
if( report_errors( m_LuaState, s ) )
{
LOGERROR("Can't load plugin %s", m_FileName.c_str() );
lua_close( m_LuaState );
m_LuaState = 0;
return false;
}
s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
if( report_errors( m_LuaState, s ) )
{
LOGERROR("Error in plugin %s", m_FileName.c_str() );
lua_close( m_LuaState );
m_LuaState = 0;
return false;
}
return true;
}
void cPlugin_Lua::AddPlugin( cPlugin* a_Plugin )
{
m_Plugins.push_back( a_Plugin );
}
void cPlugin_Lua::RemovePlugin( cPlugin* a_Plugin )
{
m_Plugins.remove( a_Plugin );
cRoot::Get()->GetPluginManager()->RemovePlugin( a_Plugin, true );
}
void cPlugin_Lua::UnloadPlugins()
{
while( m_Plugins.begin() != m_Plugins.end() )
{
RemovePlugin( *m_Plugins.begin() );
}
}
lua_State* cPlugin_Lua::GetLuaState()
{
return m_LuaState;
}

34
source/cPlugin_Lua.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <string>
#include <list>
class cPickup;
class cPlayer;
class cPacket_BlockPlace;
class cPacket_BlockDig;
class cPacket_Login;
class cPlugin;
class cPlugin_Lua //tolua_export
{ //tolua_export
public:
cPlugin_Lua(const char* a_Plugin);
~cPlugin_Lua();
virtual bool Initialize();
std::string GetFileName() { return m_FileName; } //tolua_export
typedef struct lua_State lua_State;
lua_State* GetLuaState();
void AddPlugin( cPlugin* a_Plugin );
void RemovePlugin( cPlugin* a_Plugin );
private:
void UnloadPlugins();
std::string m_FileName;
lua_State* m_LuaState;
typedef std::list< cPlugin* > PluginList;
PluginList m_Plugins;
}; //tolua_export

458
source/cRecipeChecker.cpp Normal file
View File

@ -0,0 +1,458 @@
#include "cRecipeChecker.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <list>
#ifndef _WIN32
#include <cstring>
#include <cstdlib>
#endif
#include "Defines.h"
#include "cMCLogger.h"
#include "cRoot.h"
typedef std::list< cRecipeChecker::Recipe* > RecipeList;
struct cRecipeChecker::sRecipeCheckerState
{
RecipeList Recipes;
};
cRecipeChecker* cRecipeChecker::GetRecipeChecker()
{
LOGWARN("WARNING: Using deprecated function cRecipeChecker::GetRecipeChecker() use cRoot::Get()->GetRecipeChecker() instead!");
return cRoot::Get()->GetRecipeChecker();
}
cRecipeChecker::Recipe::~Recipe()
{
delete [] Slots;
Slots = 0;
}
cRecipeChecker::~cRecipeChecker()
{
ClearRecipes();
delete m_pState;
}
cRecipeChecker::cRecipeChecker()
: m_pState( new sRecipeCheckerState )
{
ReloadRecipes();
}
void cRecipeChecker::ClearRecipes()
{
while( m_pState->Recipes.size() > 0 )
{
delete *m_pState->Recipes.begin();
m_pState->Recipes.remove( *m_pState->Recipes.begin() );
}
}
void PrintRecipe( std::vector< cRecipeChecker::RecipeSlot > & RecipeSlots )
{
LOG("Recipe:");
for(unsigned int i = 0; i < RecipeSlots.size(); i++)
{
cRecipeChecker::RecipeSlot Slot = RecipeSlots[i];
LOG("x:%i y:%i id:%i #%i", Slot.x, Slot.y, Slot.Item.m_ItemID, Slot.Item.m_ItemCount );
}
}
void PrintNear( std::ifstream & f, int a_History = 64 )
{
f.clear();
// Calculate how far we can move pointer back
std::streamoff Position = f.tellg();
f.seekg( 0, std::ios_base::beg );
std::streamoff Difference = Position - f.tellg();
if( Difference > a_History ) Difference = a_History;
f.seekg( Position - Difference, std::ios_base::beg );
std::string Near;
if( f.good() )
{
while( f.good() && Near.size() < (unsigned int)Difference )
{
char c;
f.get(c);
Near.push_back( c );
}
}
LOGERROR("Error near: \"%s\"", Near.c_str() );
}
void cRecipeChecker::ReloadRecipes()
{
LOG("--Loading recipes--");
ClearRecipes();
/*
char a_File[] = "recipes.txt";
FILE* f = 0;
#ifdef _WIN32
if( fopen_s(&f, a_File, "rb" ) == 0 ) // no error
#else
if( (f = fopen(a_File, "rb" )) != 0 ) // no error
#endif
{
char c;
while( fread( &c, sizeof(char), 1, f) == 1 )
{
}
}
else
{
LOG("Could not open file for recipes: %s", a_File);
return;
}
*/
std::ifstream f;
char a_File[] = "recipes.txt";
f.open(a_File, std::ios::in);
std::string input;
if( !f.good() )
{
f.close();
LOG("Could not open file for recipes: %s", a_File);
return;
}
std::vector< RecipeSlot > RecipeSlots;
bool bError = false;
while( f.good() )
{
bool bLoadSlot = false;
bool bLoadResult = false;
char c;
f >> c;
f.unget();
if( c == '#' )
{
//LOG("Ignoring comment");
while( f.good() && c != '\n' )
{
f.get( c );
}
continue;
}
f.get( c );
while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); }
if( f.eof() ) break;
f.unget();
int width, height;
f >> width;
f >> c; if( c != 'x' ) { bError=true; break; }
f >> height;
f >> c;
if( c == ',' ) bLoadSlot = true;
while( f.good() && bLoadSlot )
{
bool bDontAddRecipe = false;
int x, y, ItemID, Amount;
if( f.peek() == '*' )
{
f >> c;
x = -1;
}
else
{
f >> x;
}
f >> c; if( c != ':' ) { bError=true; break; }
if( f.peek() == '*' )
{
f >> c;
y = -1;
}
else
{
f >> y;
}
f >> c; if( c != ':' ) { bError=true; break; }
f >> ItemID;
f >> c; if( c != ':' ) { bError=true; break; }
f >> Amount;
f >> c;
if( c == '@' ) bLoadResult = true;
if( c != ',' ) bLoadSlot = false;
if( !isValidItem( ItemID ) )
{
LOGERROR("Error in recipes file (%s): Invalid Item ID %i", a_File, ItemID );
bDontAddRecipe = true;
}
if( x < 0 && y < 0 )
{
if( Amount < 0 )
{
LOGERROR("Error in recipes file (%s): Invalid use of negative amount on wildcard coordinates", a_File );
bDontAddRecipe = true;
}
for(int x = 0; x < width; ++x) for(int y = 0; y < height; ++y )
{
cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount );
RecipeSlot Slot;
Slot.Item = Item;
Slot.x = x;
Slot.y = y;
RecipeSlots.push_back( Slot );
}
}
else if( x < 0 )
{
if( Amount < 0 )
{
for(int x = 0; x < width; ++x) for(int yy = 0; yy < height; ++yy )
{
if( yy == y-1 ) continue;
cItem Item( (ENUM_ITEM_ID)ItemID, (char)abs(Amount) );
RecipeSlot Slot;
Slot.Item = Item;
Slot.x = x;
Slot.y = yy;
RecipeSlots.push_back( Slot );
}
}
else
{
for(int x = 0; x < width; ++x)
{
cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount );
RecipeSlot Slot;
Slot.Item = Item;
Slot.x = x;
Slot.y = y-1;
RecipeSlots.push_back( Slot );
}
}
}
else if( y < 0 )
{
if( Amount < 0 )
{
for(int xx = 0; xx < width; ++xx) for(int y = 0; y < height; ++y )
{
if( xx == x-1 ) continue;
cItem Item( (ENUM_ITEM_ID)ItemID, (char)abs(Amount) );
RecipeSlot Slot;
Slot.Item = Item;
Slot.x = xx;
Slot.y = y;
RecipeSlots.push_back( Slot );
}
}
else
{
for(int y = 0; y < height; ++y)
{
cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount );
RecipeSlot Slot;
Slot.Item = Item;
Slot.x = x-1;
Slot.y = y;
RecipeSlots.push_back( Slot );
}
}
}
else
{
if( Amount < 0 )
{
for(int xx = 0; xx < width; ++xx) for(int yy = 0; yy < height; ++yy )
{
if( xx == x-1 && yy == y-1 ) continue;
cItem Item( (ENUM_ITEM_ID)ItemID, (char)abs(Amount) );
RecipeSlot Slot;
Slot.Item = Item;
Slot.x = xx;
Slot.y = yy;
RecipeSlots.push_back( Slot );
}
}
else
{
cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount );
RecipeSlot Slot;
Slot.Item = Item;
Slot.x = x-1;
Slot.y = y-1;
RecipeSlots.push_back( Slot );
}
}
//LOG("%i %i %i %i", x, y, ItemID, Amount );
if( bLoadResult )
{
bLoadResult = false;
f >> ItemID;
f >> c; if( c != ':' ) { bError=true; break; }
f >> Amount;
//LOG("%i %i", ItemID, Amount );
if( !isValidItem( ItemID ) )
{
LOGERROR("Error in recipes file (%s): Invalid Item ID %i", a_File, ItemID );
bDontAddRecipe = true;
}
// Do a sanity check - Handshake algorithm :)
bool bDuplicateEntries = false;
for(unsigned int i = 0; i < RecipeSlots.size(); i++)
{
for(unsigned int j = i+1; j < RecipeSlots.size(); j++)
{
if( RecipeSlots[i].x == RecipeSlots[j].x && RecipeSlots[i].y == RecipeSlots[j].y )
{
LOGERROR("Error in recipes file (%s): Duplicate item on coordinates %i:%i", a_File, RecipeSlots[i].x+1, RecipeSlots[i].y+1 );
bDontAddRecipe = true;
bDuplicateEntries = true;
break;
}
}
}
if( bDuplicateEntries )
{
PrintNear( f, 64 );
PrintRecipe( RecipeSlots );
}
if( bDontAddRecipe == false )
{
cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount );
Recipe* recipe = new Recipe;
recipe->Result = Item;
recipe->NumItems = RecipeSlots.size();
recipe->Slots = new RecipeSlot[ recipe->NumItems ];
memcpy( recipe->Slots, &RecipeSlots[0], sizeof(RecipeSlot)*recipe->NumItems );
m_pState->Recipes.push_back( recipe );
//LOG("Loaded recipe for %i times %i", Amount, ItemID );
}
RecipeSlots.clear();
}
}
if( bError ) break;
}
if( bError || ( !f.eof() && !f.good() ) )
{
LOGERROR("Error: Wrong format");
PrintNear( f, 128 );
}
f.close();
LOG("Found %i recipes", m_pState->Recipes.size() );
// for(RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr )
// {
// LOG("Recipe for %i times %i", (*itr)->Result.m_ItemCount, (*itr)->Result.m_ItemID );
// for(unsigned int j = 0; j < (*itr)->NumItems; j++)
// {
// RecipeSlot Slot = (*itr)->Slots[j];
// LOG("%i %i %i %i", Slot.x, Slot.y, Slot.Item.m_ItemID, Slot.Item.m_ItemCount );
// }
// }
LOG("--Done loading recipes--");
}
cItem cRecipeChecker::CookIngredients( cItem* a_Items, int a_Width, int a_Height, bool a_bConsumeIngredients /* = false */ )
{
int iLeft = 999, iTop = 999;
int iRight = 0, iBottom = 0;
for(int y = 0; y < a_Height; y++ ) for(int x = 0; x < a_Width; x++)
{
cItem Item = a_Items[x + y*a_Width];
if( Item.m_ItemID != E_ITEM_EMPTY && Item.m_ItemCount > 0 )
{
iRight = MAX(x, iRight);
iBottom = MAX(y, iBottom);
iLeft = MIN(x, iLeft);
iTop = MIN(y, iTop);
}
}
for(RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr )
{
Recipe* recipe = (*itr);
int Left = 999, Top = 999;
int Right = 0, Bottom = 0;
for(unsigned int i = 0; i < recipe->NumItems; i++)
{
Right = MAX(recipe->Slots[i].x, Right);
Bottom = MAX(recipe->Slots[i].y, Bottom);
Left = MIN(recipe->Slots[i].x, Left);
Top = MIN(recipe->Slots[i].y, Top);
}
if( Right-Left != iRight-iLeft || Bottom-Top != iBottom-iTop ) continue;
// it has the right dimensions
// Check for empty spaces
unsigned int Hash = 0;
for(unsigned int i = 0; i < recipe->NumItems; i++)
{
int x = recipe->Slots[i].x - Left + iLeft +1;
int y = recipe->Slots[i].y - Top + iTop +1;
Hash += x + y * a_Width;
}
for(int y = 0; y < a_Height; y++ ) for(int x = 0; x < a_Width; x++)
{
cItem & Item = a_Items[x + y*a_Width];
if( Item.m_ItemID != E_ITEM_EMPTY && Item.m_ItemCount > 0 )
{
Hash -= (x+1) + (y+1)*a_Width;
}
}
if( Hash != 0 ) continue; // Empty spaces not in right place
bool bWrong = false;
for(unsigned int i = 0; i < recipe->NumItems; i++)
{
int x = recipe->Slots[i].x - Left + iLeft;
int y = recipe->Slots[i].y - Top + iTop;
cItem Item = a_Items[x + y*a_Width];
if( Item.m_ItemID != recipe->Slots[i].Item.m_ItemID
|| Item.m_ItemCount < recipe->Slots[i].Item.m_ItemCount )
{
bWrong = true;
break;
}
}
if( bWrong ) continue;
cItem Dish = recipe->Result;
// else
if( a_bConsumeIngredients )
{
// Consume! nomnom~
for(unsigned int i = 0; i < recipe->NumItems; i++)
{
int x = recipe->Slots[i].x - Left + iLeft;
int y = recipe->Slots[i].y - Top + iTop;
a_Items[x + y*a_Width].m_ItemCount -= recipe->Slots[i].Item.m_ItemCount;
if( a_Items[x + y*a_Width].m_ItemCount <= 0 ) a_Items[x + y*a_Width].Empty();
}
Dish = CookIngredients( a_Items, a_Width, a_Height, false );
}
// Return the resulting dish!
return Dish;
}
return cItem();
}

39
source/cRecipeChecker.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include "cItem.h"
class cRecipeChecker
{
public:
static cRecipeChecker * GetRecipeChecker();
// Grid of cItems of a_Width width and a_Height Height
cItem CookIngredients( cItem* a_Items, int a_Width, int a_Height, bool a_bConsumeIngredients = false );
struct RecipeSlot
{
cItem Item;
int x, y;
};
struct Recipe
{
Recipe() : Slots( 0 ), NumItems( 0 ) {}
~Recipe();
RecipeSlot* Slots; // Array of RecipeSlots
unsigned int NumItems;
cItem Result;
};
void ReloadRecipes();
static void DeleteMe();
private:
friend class cRoot;
cRecipeChecker();
~cRecipeChecker();
struct sRecipeCheckerState;
sRecipeCheckerState* m_pState;
void ClearRecipes();
};

View File

@ -0,0 +1,36 @@
#include "cReferenceManager.h"
#include "cEntity.h"
cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type )
: m_Type( a_Type )
{
}
cReferenceManager::~cReferenceManager()
{
if( m_Type == RFMNGR_REFERENCERS )
{
for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr )
{
*(*itr) = 0; // Set referenced pointer to 0
}
}
else
{
for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr )
{
cEntity* Ptr = (*(*itr));
if( Ptr ) Ptr->Dereference( *(*itr) );
}
}
}
void cReferenceManager::AddReference( cEntity*& a_EntityPtr )
{
m_References.push_back( &a_EntityPtr );
}
void cReferenceManager::Dereference( cEntity*& a_EntityPtr )
{
m_References.remove( &a_EntityPtr );
}

Some files were not shown because too many files have changed in this diff Show More