Attempt to bring sanity to newlines across systems.
git-svn-id: http://mc-server.googlecode.com/svn/trunk@606 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
|
||||
// Globals.cpp
|
||||
|
||||
// This file is used for precompiled header generation in MSVC environments
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Globals.cpp
|
||||
|
||||
// This file is used for precompiled header generation in MSVC environments
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,372 +1,372 @@
|
||||
/*
|
||||
Socket.cpp
|
||||
|
||||
Copyright (C) 2002-2004 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Socket.h"
|
||||
#include <iostream>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <cstring>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#define SD_SEND 0x01
|
||||
#else
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
|
||||
#ifdef __MAC_NA
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif // MSG_NOSIGNAL
|
||||
|
||||
using namespace std;
|
||||
|
||||
int Socket::nofSockets_= 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::Start()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!nofSockets_)
|
||||
{
|
||||
WSADATA info;
|
||||
if (WSAStartup(MAKEWORD(2,0), &info))
|
||||
{
|
||||
throw "Could not start WSA";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++nofSockets_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::End()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::Socket() :
|
||||
s_(INVALID_SOCKET)
|
||||
{
|
||||
Start();
|
||||
// UDP: use SOCK_DGRAM instead of SOCK_STREAM
|
||||
s_ = socket(AF_INET,SOCK_STREAM,0);
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
throw "INVALID_SOCKET";
|
||||
}
|
||||
|
||||
refCounter_ = new int(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::Socket(SOCKET s) : s_(s)
|
||||
{
|
||||
Start();
|
||||
refCounter_ = new int(1);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::~Socket()
|
||||
{
|
||||
if (! --(*refCounter_))
|
||||
{
|
||||
Close();
|
||||
delete refCounter_;
|
||||
}
|
||||
|
||||
--nofSockets_;
|
||||
if (!nofSockets_)
|
||||
{
|
||||
End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::Socket(const Socket& o)
|
||||
{
|
||||
refCounter_=o.refCounter_;
|
||||
(*refCounter_)++;
|
||||
s_ =o.s_;
|
||||
|
||||
nofSockets_++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket& Socket::operator=(Socket& o)
|
||||
{
|
||||
(*o.refCounter_)++;
|
||||
|
||||
refCounter_ = o.refCounter_;
|
||||
s_ = o.s_;
|
||||
|
||||
nofSockets_++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool Socket::IsValid(void) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (s_ != INVALID_SOCKET);
|
||||
#else
|
||||
return (s_ >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::Close( bool a_WaitSend /* = false */ )
|
||||
{
|
||||
if (IsValid())
|
||||
{
|
||||
if (a_WaitSend)
|
||||
{
|
||||
assert( shutdown(s_, SD_SEND ) == 0 );
|
||||
char c;
|
||||
while( recv(s_, &c, 1, 0 ) > 0 )
|
||||
{}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// Linux needs to shut the socket down first, otherwise the socket keeps blocking accept() and select() calls!
|
||||
// Ref.: http://forum.mc-server.org/showthread.php?tid=344
|
||||
if (shutdown(s_, SHUT_RDWR) != 0)
|
||||
{
|
||||
LOGWARN("Error on shutting down socket %d", s_);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
closesocket(s_);
|
||||
|
||||
s_ = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Socket::ReceiveLine()
|
||||
{
|
||||
std::string ret;
|
||||
while (1)
|
||||
{
|
||||
char r;
|
||||
|
||||
if (recv(s_, &r, 1, 0) <= 0 )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
ret += r;
|
||||
if (r == '\n') return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Socket::ReceiveBytes( unsigned int a_Length )
|
||||
{
|
||||
std::string ret;
|
||||
while( ret.size() < a_Length )
|
||||
{
|
||||
char r;
|
||||
|
||||
if (recv(s_, &r, 1, 0) <= 0 )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
ret += r;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::SendLine(std::string s)
|
||||
{
|
||||
s += '\n';
|
||||
if( send(s_,s.c_str(),s.length(),MSG_NOSIGNAL) <= 0 )
|
||||
{
|
||||
//printf("SendLine Socket error!! \n" );
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::SendBytes(const std::string& s)
|
||||
{
|
||||
if( send(s_,s.c_str(),s.length(), MSG_NOSIGNAL) <= 0 )
|
||||
{
|
||||
//printf("SendBytes Socket error!! \n" );
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SocketServer::SocketServer(int port, int connections, TypeSocket type)
|
||||
{
|
||||
sockaddr_in sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
|
||||
sa.sin_family = PF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
s_ = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
LOG("WebServer: INVALID_SOCKET");
|
||||
}
|
||||
|
||||
if(type==NonBlockingSocket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
char yes = 1;
|
||||
#else
|
||||
int yes = 1;
|
||||
#endif
|
||||
|
||||
if (setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
|
||||
{
|
||||
LOG("WebServer: setsockopt == -1");
|
||||
return;
|
||||
}
|
||||
|
||||
/* bind the socket to the internet address */
|
||||
if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR)
|
||||
{
|
||||
closesocket(s_);
|
||||
LOG("WebServer: INVALID_SOCKET");
|
||||
}
|
||||
|
||||
listen(s_, connections);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket* SocketServer::Accept()
|
||||
{
|
||||
Socket * r = new Socket(accept(s_, 0, 0));
|
||||
if (!r->IsValid())
|
||||
{
|
||||
delete r;
|
||||
r = NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type)
|
||||
{
|
||||
FD_ZERO(&fds_);
|
||||
SOCKET Highest = s1->s_;
|
||||
FD_SET(const_cast<Socket*>(s1)->s_,&fds_);
|
||||
if(s2)
|
||||
{
|
||||
FD_SET(const_cast<Socket*>(s2)->s_,&fds_);
|
||||
if (s2->s_ > Highest)
|
||||
{
|
||||
Highest = s2->s_;
|
||||
}
|
||||
}
|
||||
if (select(Highest + 1, &fds_, NULL, NULL, NULL) == SOCKET_ERROR)
|
||||
{
|
||||
throw "Error in select";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool SocketSelect::Readable(Socket const* const s)
|
||||
{
|
||||
return (FD_ISSET(s->s_,&fds_) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Socket.cpp
|
||||
|
||||
Copyright (C) 2002-2004 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Socket.h"
|
||||
#include <iostream>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <cstring>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#define SD_SEND 0x01
|
||||
#else
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
|
||||
#ifdef __MAC_NA
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif // MSG_NOSIGNAL
|
||||
|
||||
using namespace std;
|
||||
|
||||
int Socket::nofSockets_= 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::Start()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!nofSockets_)
|
||||
{
|
||||
WSADATA info;
|
||||
if (WSAStartup(MAKEWORD(2,0), &info))
|
||||
{
|
||||
throw "Could not start WSA";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++nofSockets_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::End()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::Socket() :
|
||||
s_(INVALID_SOCKET)
|
||||
{
|
||||
Start();
|
||||
// UDP: use SOCK_DGRAM instead of SOCK_STREAM
|
||||
s_ = socket(AF_INET,SOCK_STREAM,0);
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
throw "INVALID_SOCKET";
|
||||
}
|
||||
|
||||
refCounter_ = new int(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::Socket(SOCKET s) : s_(s)
|
||||
{
|
||||
Start();
|
||||
refCounter_ = new int(1);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::~Socket()
|
||||
{
|
||||
if (! --(*refCounter_))
|
||||
{
|
||||
Close();
|
||||
delete refCounter_;
|
||||
}
|
||||
|
||||
--nofSockets_;
|
||||
if (!nofSockets_)
|
||||
{
|
||||
End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket::Socket(const Socket& o)
|
||||
{
|
||||
refCounter_=o.refCounter_;
|
||||
(*refCounter_)++;
|
||||
s_ =o.s_;
|
||||
|
||||
nofSockets_++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket& Socket::operator=(Socket& o)
|
||||
{
|
||||
(*o.refCounter_)++;
|
||||
|
||||
refCounter_ = o.refCounter_;
|
||||
s_ = o.s_;
|
||||
|
||||
nofSockets_++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool Socket::IsValid(void) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (s_ != INVALID_SOCKET);
|
||||
#else
|
||||
return (s_ >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::Close( bool a_WaitSend /* = false */ )
|
||||
{
|
||||
if (IsValid())
|
||||
{
|
||||
if (a_WaitSend)
|
||||
{
|
||||
assert( shutdown(s_, SD_SEND ) == 0 );
|
||||
char c;
|
||||
while( recv(s_, &c, 1, 0 ) > 0 )
|
||||
{}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// Linux needs to shut the socket down first, otherwise the socket keeps blocking accept() and select() calls!
|
||||
// Ref.: http://forum.mc-server.org/showthread.php?tid=344
|
||||
if (shutdown(s_, SHUT_RDWR) != 0)
|
||||
{
|
||||
LOGWARN("Error on shutting down socket %d", s_);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
closesocket(s_);
|
||||
|
||||
s_ = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Socket::ReceiveLine()
|
||||
{
|
||||
std::string ret;
|
||||
while (1)
|
||||
{
|
||||
char r;
|
||||
|
||||
if (recv(s_, &r, 1, 0) <= 0 )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
ret += r;
|
||||
if (r == '\n') return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Socket::ReceiveBytes( unsigned int a_Length )
|
||||
{
|
||||
std::string ret;
|
||||
while( ret.size() < a_Length )
|
||||
{
|
||||
char r;
|
||||
|
||||
if (recv(s_, &r, 1, 0) <= 0 )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
ret += r;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::SendLine(std::string s)
|
||||
{
|
||||
s += '\n';
|
||||
if( send(s_,s.c_str(),s.length(),MSG_NOSIGNAL) <= 0 )
|
||||
{
|
||||
//printf("SendLine Socket error!! \n" );
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Socket::SendBytes(const std::string& s)
|
||||
{
|
||||
if( send(s_,s.c_str(),s.length(), MSG_NOSIGNAL) <= 0 )
|
||||
{
|
||||
//printf("SendBytes Socket error!! \n" );
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SocketServer::SocketServer(int port, int connections, TypeSocket type)
|
||||
{
|
||||
sockaddr_in sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
|
||||
sa.sin_family = PF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
s_ = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
LOG("WebServer: INVALID_SOCKET");
|
||||
}
|
||||
|
||||
if(type==NonBlockingSocket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
char yes = 1;
|
||||
#else
|
||||
int yes = 1;
|
||||
#endif
|
||||
|
||||
if (setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
|
||||
{
|
||||
LOG("WebServer: setsockopt == -1");
|
||||
return;
|
||||
}
|
||||
|
||||
/* bind the socket to the internet address */
|
||||
if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR)
|
||||
{
|
||||
closesocket(s_);
|
||||
LOG("WebServer: INVALID_SOCKET");
|
||||
}
|
||||
|
||||
listen(s_, connections);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Socket* SocketServer::Accept()
|
||||
{
|
||||
Socket * r = new Socket(accept(s_, 0, 0));
|
||||
if (!r->IsValid())
|
||||
{
|
||||
delete r;
|
||||
r = NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type)
|
||||
{
|
||||
FD_ZERO(&fds_);
|
||||
SOCKET Highest = s1->s_;
|
||||
FD_SET(const_cast<Socket*>(s1)->s_,&fds_);
|
||||
if(s2)
|
||||
{
|
||||
FD_SET(const_cast<Socket*>(s2)->s_,&fds_);
|
||||
if (s2->s_ > Highest)
|
||||
{
|
||||
Highest = s2->s_;
|
||||
}
|
||||
}
|
||||
if (select(Highest + 1, &fds_, NULL, NULL, NULL) == SOCKET_ERROR)
|
||||
{
|
||||
throw "Error in select";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool SocketSelect::Readable(Socket const* const s)
|
||||
{
|
||||
return (FD_ISSET(s->s_,&fds_) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
/*
|
||||
stdHelpers.cpp
|
||||
|
||||
Copyright (C) 2002-2004 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "StdHelpers.h"
|
||||
#include <cctype>
|
||||
|
||||
std::string ReplaceInStr(const std::string& in, const std::string& search_for, const std::string& replace_with) {
|
||||
std::string ret = in;
|
||||
|
||||
std::string::size_type pos = ret.find(search_for);
|
||||
|
||||
while (pos != std::string::npos) {
|
||||
ret = ret.replace(pos, search_for.size(), replace_with);
|
||||
pos = pos - search_for.size() + replace_with.size() + 1;
|
||||
pos = ret.find(search_for, pos);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// std:toupper and std::tolower are overloaded. Well...
|
||||
// http://gcc.gnu.org/ml/libstdc++/2002-11/msg00180.html
|
||||
char toLower_ (char c) { return std::tolower(c); }
|
||||
char toUpper_ (char c) { return std::toupper(c); }
|
||||
|
||||
void ToUpper(std::string& s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(),toUpper_);
|
||||
}
|
||||
|
||||
void ToLower(std::string& s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(),toLower_);
|
||||
}
|
||||
/*
|
||||
stdHelpers.cpp
|
||||
|
||||
Copyright (C) 2002-2004 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "StdHelpers.h"
|
||||
#include <cctype>
|
||||
|
||||
std::string ReplaceInStr(const std::string& in, const std::string& search_for, const std::string& replace_with) {
|
||||
std::string ret = in;
|
||||
|
||||
std::string::size_type pos = ret.find(search_for);
|
||||
|
||||
while (pos != std::string::npos) {
|
||||
ret = ret.replace(pos, search_for.size(), replace_with);
|
||||
pos = pos - search_for.size() + replace_with.size() + 1;
|
||||
pos = ret.find(search_for, pos);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// std:toupper and std::tolower are overloaded. Well...
|
||||
// http://gcc.gnu.org/ml/libstdc++/2002-11/msg00180.html
|
||||
char toLower_ (char c) { return std::tolower(c); }
|
||||
char toUpper_ (char c) { return std::toupper(c); }
|
||||
|
||||
void ToUpper(std::string& s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(),toUpper_);
|
||||
}
|
||||
|
||||
void ToLower(std::string& s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(),toLower_);
|
||||
}
|
||||
|
||||
@@ -1,165 +1,165 @@
|
||||
/*
|
||||
UrlHelper.cpp
|
||||
|
||||
Copyright (C) 2002-2004 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "UrlHelper.h"
|
||||
#include "Tracer.h"
|
||||
#include "StdHelpers.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
bool RemoveProtocolFromUrl(std::string const& url, std::string& protocol, std::string& rest) {
|
||||
TraceFunc("RemoveProtocolFromUrl");
|
||||
Trace(std::string("url='")+url+"'");
|
||||
std::string::size_type pos_colon = url.find(":");
|
||||
|
||||
if (pos_colon == std::string::npos) {
|
||||
rest = url;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url.size() < pos_colon + 2) {
|
||||
rest = url;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url[pos_colon+1] != '/' ||
|
||||
url[pos_colon+2] != '/') {
|
||||
rest = url;
|
||||
return false;
|
||||
}
|
||||
|
||||
protocol = url.substr(0,pos_colon);
|
||||
rest = url.substr(3+pos_colon); // Skipping three characters ( '://' )
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SplitGetReq(std::string get_req, std::string& path, std::map<std::string, std::string>& params) {
|
||||
TraceFunc("SplitGetReq");
|
||||
|
||||
// Remove trailing newlines
|
||||
if (get_req[get_req.size()-1] == '\x0d' ||
|
||||
get_req[get_req.size()-1] == '\x0a')
|
||||
get_req=get_req.substr(0, get_req.size()-1);
|
||||
|
||||
if (get_req[get_req.size()-1] == '\x0d' ||
|
||||
get_req[get_req.size()-1] == '\x0a')
|
||||
get_req=get_req.substr(0, get_req.size()-1);
|
||||
|
||||
// Remove potential Trailing HTTP/1.x
|
||||
if (get_req.size() > 7) {
|
||||
if (get_req.substr(get_req.size()-8, 7) == "HTTP/1.") {
|
||||
get_req=get_req.substr(0, get_req.size()-9);
|
||||
}
|
||||
}
|
||||
|
||||
std::string::size_type qm = get_req.find("?");
|
||||
if (qm != std::string::npos) {
|
||||
std::string url_params = get_req.substr(qm+1);
|
||||
|
||||
path = get_req.substr(0, qm);
|
||||
|
||||
// Appending a '&' so that there are as many '&' as name-value pairs.
|
||||
// It makes it easier to split the url for name value pairs, he he he
|
||||
url_params += "&";
|
||||
|
||||
std::string::size_type next_amp = url_params.find("&");
|
||||
|
||||
while (next_amp != std::string::npos) {
|
||||
std::string name_value = url_params.substr(0,next_amp);
|
||||
url_params = url_params.substr(next_amp+1);
|
||||
next_amp = url_params.find("&");
|
||||
|
||||
std::string::size_type pos_equal = name_value.find("=");
|
||||
|
||||
std::string nam = name_value.substr(0,pos_equal);
|
||||
std::string val = name_value.substr(pos_equal+1);
|
||||
|
||||
std::string::size_type pos_plus;
|
||||
while ( (pos_plus = val.find("+")) != std::string::npos ) {
|
||||
val.replace(pos_plus, 1, " ");
|
||||
}
|
||||
|
||||
// Replacing %xy notation
|
||||
std::string::size_type pos_hex = 0;
|
||||
while ( (pos_hex = val.find("%", pos_hex)) != std::string::npos ) {
|
||||
std::stringstream h;
|
||||
h << val.substr(pos_hex+1, 2);
|
||||
h << std::hex;
|
||||
|
||||
int i;
|
||||
h>>i;
|
||||
|
||||
std::stringstream f;
|
||||
f << static_cast<char>(i);
|
||||
std::string s;
|
||||
f >> s;
|
||||
|
||||
val.replace(pos_hex, 3, s);
|
||||
pos_hex ++;
|
||||
}
|
||||
|
||||
params.insert(std::map<std::string,std::string>::value_type(nam, val));
|
||||
}
|
||||
}
|
||||
else {
|
||||
path = get_req;
|
||||
}
|
||||
}
|
||||
|
||||
void SplitUrl(std::string const& url, std::string& protocol, std::string& server, std::string& path) {
|
||||
TraceFunc("SplitUrl");
|
||||
RemoveProtocolFromUrl(url, protocol, server);
|
||||
|
||||
if (protocol == "http") {
|
||||
std::string::size_type pos_slash = server.find("/");
|
||||
|
||||
if (pos_slash != std::string::npos) {
|
||||
Trace("slash found");
|
||||
path = server.substr(pos_slash);
|
||||
server = server.substr(0, pos_slash);
|
||||
}
|
||||
else {
|
||||
Trace("slash not found");
|
||||
path = "/";
|
||||
}
|
||||
}
|
||||
else if (protocol == "file") {
|
||||
path = ReplaceInStr(server, "\\", "/");
|
||||
server = "";
|
||||
}
|
||||
else {
|
||||
std::cerr << "unknown protocol in SplitUrl: '" << protocol << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
/*
|
||||
UrlHelper.cpp
|
||||
|
||||
Copyright (C) 2002-2004 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "UrlHelper.h"
|
||||
#include "Tracer.h"
|
||||
#include "StdHelpers.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
bool RemoveProtocolFromUrl(std::string const& url, std::string& protocol, std::string& rest) {
|
||||
TraceFunc("RemoveProtocolFromUrl");
|
||||
Trace(std::string("url='")+url+"'");
|
||||
std::string::size_type pos_colon = url.find(":");
|
||||
|
||||
if (pos_colon == std::string::npos) {
|
||||
rest = url;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url.size() < pos_colon + 2) {
|
||||
rest = url;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url[pos_colon+1] != '/' ||
|
||||
url[pos_colon+2] != '/') {
|
||||
rest = url;
|
||||
return false;
|
||||
}
|
||||
|
||||
protocol = url.substr(0,pos_colon);
|
||||
rest = url.substr(3+pos_colon); // Skipping three characters ( '://' )
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SplitGetReq(std::string get_req, std::string& path, std::map<std::string, std::string>& params) {
|
||||
TraceFunc("SplitGetReq");
|
||||
|
||||
// Remove trailing newlines
|
||||
if (get_req[get_req.size()-1] == '\x0d' ||
|
||||
get_req[get_req.size()-1] == '\x0a')
|
||||
get_req=get_req.substr(0, get_req.size()-1);
|
||||
|
||||
if (get_req[get_req.size()-1] == '\x0d' ||
|
||||
get_req[get_req.size()-1] == '\x0a')
|
||||
get_req=get_req.substr(0, get_req.size()-1);
|
||||
|
||||
// Remove potential Trailing HTTP/1.x
|
||||
if (get_req.size() > 7) {
|
||||
if (get_req.substr(get_req.size()-8, 7) == "HTTP/1.") {
|
||||
get_req=get_req.substr(0, get_req.size()-9);
|
||||
}
|
||||
}
|
||||
|
||||
std::string::size_type qm = get_req.find("?");
|
||||
if (qm != std::string::npos) {
|
||||
std::string url_params = get_req.substr(qm+1);
|
||||
|
||||
path = get_req.substr(0, qm);
|
||||
|
||||
// Appending a '&' so that there are as many '&' as name-value pairs.
|
||||
// It makes it easier to split the url for name value pairs, he he he
|
||||
url_params += "&";
|
||||
|
||||
std::string::size_type next_amp = url_params.find("&");
|
||||
|
||||
while (next_amp != std::string::npos) {
|
||||
std::string name_value = url_params.substr(0,next_amp);
|
||||
url_params = url_params.substr(next_amp+1);
|
||||
next_amp = url_params.find("&");
|
||||
|
||||
std::string::size_type pos_equal = name_value.find("=");
|
||||
|
||||
std::string nam = name_value.substr(0,pos_equal);
|
||||
std::string val = name_value.substr(pos_equal+1);
|
||||
|
||||
std::string::size_type pos_plus;
|
||||
while ( (pos_plus = val.find("+")) != std::string::npos ) {
|
||||
val.replace(pos_plus, 1, " ");
|
||||
}
|
||||
|
||||
// Replacing %xy notation
|
||||
std::string::size_type pos_hex = 0;
|
||||
while ( (pos_hex = val.find("%", pos_hex)) != std::string::npos ) {
|
||||
std::stringstream h;
|
||||
h << val.substr(pos_hex+1, 2);
|
||||
h << std::hex;
|
||||
|
||||
int i;
|
||||
h>>i;
|
||||
|
||||
std::stringstream f;
|
||||
f << static_cast<char>(i);
|
||||
std::string s;
|
||||
f >> s;
|
||||
|
||||
val.replace(pos_hex, 3, s);
|
||||
pos_hex ++;
|
||||
}
|
||||
|
||||
params.insert(std::map<std::string,std::string>::value_type(nam, val));
|
||||
}
|
||||
}
|
||||
else {
|
||||
path = get_req;
|
||||
}
|
||||
}
|
||||
|
||||
void SplitUrl(std::string const& url, std::string& protocol, std::string& server, std::string& path) {
|
||||
TraceFunc("SplitUrl");
|
||||
RemoveProtocolFromUrl(url, protocol, server);
|
||||
|
||||
if (protocol == "http") {
|
||||
std::string::size_type pos_slash = server.find("/");
|
||||
|
||||
if (pos_slash != std::string::npos) {
|
||||
Trace("slash found");
|
||||
path = server.substr(pos_slash);
|
||||
server = server.substr(0, pos_slash);
|
||||
}
|
||||
else {
|
||||
Trace("slash not found");
|
||||
path = "/";
|
||||
}
|
||||
}
|
||||
else if (protocol == "file") {
|
||||
path = ReplaceInStr(server, "\\", "/");
|
||||
server = "";
|
||||
}
|
||||
else {
|
||||
std::cerr << "unknown protocol in SplitUrl: '" << protocol << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,497 +1,497 @@
|
||||
/*
|
||||
WebServer.cpp
|
||||
|
||||
Copyright (C) 2003-2007 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
Thanks to Tom Lynn who pointed out an error in this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include <ctime>
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
#include "WebServer.h"
|
||||
#include "cEvents.h"
|
||||
#include "Socket.h"
|
||||
#include "UrlHelper.h"
|
||||
#include "base64.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
webserver::request_func webserver::request_func_ = NULL;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static std::string EatLine( std::string& a_String )
|
||||
{
|
||||
std::string RetVal = "";
|
||||
unsigned int StringSize = a_String.size();
|
||||
const char* c = a_String.c_str();
|
||||
|
||||
for( unsigned int i = 0; i < StringSize; ++i, ++c)
|
||||
{
|
||||
if( *c == '\n' )
|
||||
{
|
||||
RetVal += *c;
|
||||
// ++i; ++c;
|
||||
// if( i < StringSize )
|
||||
// {
|
||||
// if( *c == '\r' )
|
||||
// {
|
||||
// RetVal += *c;
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
}
|
||||
RetVal += *c;
|
||||
}
|
||||
a_String = a_String.substr( RetVal.size() );
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Turns
|
||||
// "blabla my string with \"quotes\"!"
|
||||
// into
|
||||
// blabla my string with "quotes"!
|
||||
static std::string GetQuotedString( const std::string& a_String )
|
||||
{
|
||||
std::string RetVal;
|
||||
|
||||
bool bGotFirstQuote = false;
|
||||
bool bIgnoreNext = false;
|
||||
unsigned int StrLen = a_String.size();
|
||||
for( unsigned int i = 0; i < StrLen; ++i )
|
||||
{
|
||||
if( bIgnoreNext == false )
|
||||
{
|
||||
if( a_String[i] == '\"' )
|
||||
{
|
||||
if( bGotFirstQuote == false )
|
||||
{
|
||||
bGotFirstQuote = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if( a_String[i] == '\\' ) // Escape character
|
||||
{
|
||||
bIgnoreNext = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RetVal.push_back( a_String[i] );
|
||||
bIgnoreNext = false;
|
||||
}
|
||||
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ParseMultipartFormData( webserver::http_request& req, Socket* s)
|
||||
{
|
||||
static const std::string multipart_form_data = "multipart/form-data";
|
||||
if(req.content_type_.substr(0, multipart_form_data.size()) == multipart_form_data) // Difficult data... :(
|
||||
{
|
||||
AStringVector ContentTypeData = StringSplit( req.content_type_, "; " );
|
||||
|
||||
std::string boundary;
|
||||
// Find boundary
|
||||
for( unsigned int i = 0; i < ContentTypeData.size(); ++i )
|
||||
{
|
||||
static const std::string boundary_ = "boundary=";
|
||||
if( ContentTypeData[i].substr(0, boundary_.size()) == boundary_ ) // Found boundary
|
||||
{
|
||||
boundary = ContentTypeData[i].substr( boundary_.size() );
|
||||
}
|
||||
}
|
||||
|
||||
//LOGINFO("Boundary: %s", boundary.c_str() );
|
||||
std::string boundary_start = "--" + boundary;
|
||||
std::string boundary_end = boundary_start + "--";
|
||||
|
||||
std::string Content = s->ReceiveBytes( req.content_length_ );
|
||||
|
||||
//printf("Total content: \n%s\n", Content.c_str() );
|
||||
|
||||
// Should start with boundary!
|
||||
std::string line = EatLine( Content );
|
||||
if( line.substr(0, boundary_start.size() ) != boundary_start )
|
||||
{
|
||||
// Something was wrong! :(
|
||||
Content.clear();
|
||||
}
|
||||
|
||||
while( !Content.empty() )
|
||||
{
|
||||
webserver::formdata FormData;
|
||||
|
||||
static const std::string content_disposition = "Content-Disposition: ";
|
||||
static const std::string content_type = "Content-Type: ";
|
||||
|
||||
std::string f_disposition;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
std::string line = EatLine( Content );
|
||||
if( line.empty() )
|
||||
break;
|
||||
|
||||
unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d");
|
||||
if (pos_cr_lf == 0) break; // Empty line, indicates end of mime thingy
|
||||
|
||||
if( line.substr(0, content_disposition.size() ) == content_disposition )
|
||||
{
|
||||
f_disposition = line.substr(content_disposition.size());
|
||||
LOGINFO("Disposition: %s", f_disposition.c_str() );
|
||||
}
|
||||
else if( line.substr(0, content_type.size() ) == content_type )
|
||||
{
|
||||
FormData.content_type_ = line.substr(content_type.size());
|
||||
}
|
||||
|
||||
//LOGINFO("Got line: '%s'", line.c_str() );
|
||||
}
|
||||
|
||||
// Check if we got the proper headers
|
||||
if( !f_disposition.empty() )
|
||||
{
|
||||
static const std::string disp_name = "name=";
|
||||
static const std::string disp_filename = "filename=";
|
||||
|
||||
// Parse the disposition
|
||||
AStringVector DispositionData = StringSplit( f_disposition, "; " );
|
||||
for( unsigned int i = 0; i < DispositionData.size(); ++i )
|
||||
{
|
||||
if( DispositionData[i].substr(0, disp_name.size()) == disp_name )
|
||||
{
|
||||
FormData.name_ = GetQuotedString( DispositionData[i].substr(disp_name.size()) );
|
||||
}
|
||||
else if( DispositionData[i].substr(0, disp_filename.size()) == disp_filename )
|
||||
{
|
||||
FormData.filename_ = GetQuotedString( DispositionData[i].substr(disp_filename.size()) );
|
||||
}
|
||||
}
|
||||
|
||||
std::string ContentValue;
|
||||
// Parse until boundary_end is found
|
||||
while( 1 )
|
||||
{
|
||||
std::string line = EatLine( Content );
|
||||
if( line.empty() )
|
||||
break;
|
||||
|
||||
if( line.substr(0, boundary_end.size() ) == boundary_end )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( line.substr(0, boundary_start.size() ) == boundary_start )
|
||||
{
|
||||
break;
|
||||
}
|
||||
ContentValue.append( line.c_str(), line.size() );
|
||||
}
|
||||
|
||||
|
||||
FormData.value_ = ContentValue;
|
||||
}
|
||||
|
||||
req.multipart_formdata_.push_back( FormData );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned webserver::Request(void* ptr_s)
|
||||
#else
|
||||
void* webserver::Request(void* ptr_s)
|
||||
#endif
|
||||
{
|
||||
Socket* s = (reinterpret_cast<Socket*>(ptr_s));
|
||||
|
||||
std::string line = s->ReceiveLine();
|
||||
if (line.empty())
|
||||
{
|
||||
s->Close();
|
||||
delete s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_request req;
|
||||
std::string path;
|
||||
std::map<std::string, std::string> params;
|
||||
size_t posStartPath = 0;
|
||||
|
||||
if (line.find("GET") == 0)
|
||||
{
|
||||
req.method_="GET";
|
||||
posStartPath = line.find_first_not_of(" ",3);
|
||||
}
|
||||
else if (line.find("POST") == 0)
|
||||
{
|
||||
req.method_="POST";
|
||||
posStartPath = line.find_first_not_of(" ",4);
|
||||
}
|
||||
|
||||
SplitGetReq(line.substr(posStartPath), path, params);
|
||||
|
||||
req.status_ = "202 OK";
|
||||
req.s_ = s;
|
||||
req.path_ = path;
|
||||
req.params_ = params;
|
||||
|
||||
static const std::string authorization = "Authorization: Basic ";
|
||||
static const std::string accept = "Accept: " ;
|
||||
static const std::string accept_language = "Accept-Language: " ;
|
||||
static const std::string accept_encoding = "Accept-Encoding: " ;
|
||||
static const std::string user_agent = "User-Agent: " ;
|
||||
static const std::string content_length = "Content-Length: " ;
|
||||
static const std::string content_type = "Content-Type: " ;
|
||||
|
||||
while(1)
|
||||
{
|
||||
line=s->ReceiveLine();
|
||||
if (line.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d");
|
||||
if (pos_cr_lf == 0) break;
|
||||
|
||||
line = line.substr(0,pos_cr_lf);
|
||||
|
||||
if (line.substr(0, authorization.size()) == authorization)
|
||||
{
|
||||
req.authentication_given_ = true;
|
||||
std::string encoded = line.substr(authorization.size());
|
||||
std::string decoded = base64_decode(encoded);
|
||||
|
||||
unsigned int pos_colon = decoded.find(":");
|
||||
|
||||
req.username_ = decoded.substr(0, pos_colon);
|
||||
req.password_ = decoded.substr(pos_colon+1 );
|
||||
}
|
||||
else if (line.substr(0, accept.size()) == accept)
|
||||
{
|
||||
req.accept_ = line.substr(accept.size());
|
||||
}
|
||||
else if (line.substr(0, accept_language.size()) == accept_language)
|
||||
{
|
||||
req.accept_language_ = line.substr(accept_language.size());
|
||||
}
|
||||
else if (line.substr(0, accept_encoding.size()) == accept_encoding)
|
||||
{
|
||||
req.accept_encoding_ = line.substr(accept_encoding.size());
|
||||
}
|
||||
else if (line.substr(0, user_agent.size()) == user_agent)
|
||||
{
|
||||
req.user_agent_ = line.substr(user_agent.size());
|
||||
}
|
||||
else if (line.substr(0, content_length.size()) == content_length)
|
||||
{
|
||||
req.content_length_ = atoi( line.substr(content_length.size()).c_str() );
|
||||
}
|
||||
else if (line.substr(0, content_type.size()) == content_type)
|
||||
{
|
||||
req.content_type_ = line.substr(content_type.size());
|
||||
}
|
||||
}
|
||||
|
||||
if( (req.method_.compare("POST") == 0) && (req.content_length_ > 0) )
|
||||
{
|
||||
// The only content type we can parse at the moment, the default HTML post data
|
||||
if( req.content_type_.compare( "application/x-www-form-urlencoded" ) == 0 )
|
||||
{
|
||||
std::string Content = s->ReceiveBytes( req.content_length_ );
|
||||
Content.insert( 0, "/ ?" ); // Little hack, inserts dummy URL so that SplitGetReq() can work with this content
|
||||
|
||||
std::string dummy;
|
||||
std::map<std::string, std::string> post_params;
|
||||
SplitGetReq(Content, dummy, post_params);
|
||||
|
||||
req.params_post_ = post_params;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseMultipartFormData( req, s );
|
||||
}
|
||||
}
|
||||
|
||||
request_func_(&req);
|
||||
|
||||
std::stringstream str_str;
|
||||
str_str << req.answer_.size();
|
||||
|
||||
time_t ltime;
|
||||
time(<ime);
|
||||
tm* gmt= gmtime(<ime);
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::string const serverName = "MCServerWebAdmin (Windows)";
|
||||
#elif __APPLE__
|
||||
static std::string const serverName = "MCServerWebAdmin (MacOSX)";
|
||||
#else
|
||||
static std::string const serverName = "MCServerWebAdmin (Linux)";
|
||||
#endif
|
||||
|
||||
|
||||
char* asctime_remove_nl = std::asctime(gmt);
|
||||
asctime_remove_nl[24] = 0;
|
||||
|
||||
s->SendBytes("HTTP/1.1 ");
|
||||
|
||||
if (! req.auth_realm_.empty() )
|
||||
{
|
||||
s->SendLine("401 Unauthorized");
|
||||
s->SendBytes("WWW-Authenticate: Basic Realm=\"");
|
||||
s->SendBytes(req.auth_realm_);
|
||||
s->SendLine("\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
s->SendLine(req.status_);
|
||||
}
|
||||
s->SendLine(std::string("Date: ") + asctime_remove_nl + " GMT");
|
||||
s->SendLine(std::string("Server: ") +serverName);
|
||||
s->SendLine("Connection: close");
|
||||
s->SendLine("Content-Type: text/html; charset=ISO-8859-1");
|
||||
s->SendLine("Content-Length: " + str_str.str());
|
||||
s->SendLine("");
|
||||
s->SendLine(req.answer_);
|
||||
|
||||
s->Close( true ); // true = wait for all data to be sent before closing
|
||||
delete s;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void webserver::Stop()
|
||||
{
|
||||
m_bStop = true;
|
||||
m_Socket->Close();
|
||||
m_Events->Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool webserver::Begin()
|
||||
{
|
||||
if (!m_Socket->IsValid())
|
||||
{
|
||||
LOGINFO("WebAdmin: The server socket is invalid. Terminating WebAdmin.");
|
||||
return false;
|
||||
}
|
||||
m_bStop = false;
|
||||
while ( !m_bStop )
|
||||
{
|
||||
Socket * ptr_s = m_Socket->Accept();
|
||||
if (m_bStop)
|
||||
{
|
||||
if (ptr_s != 0)
|
||||
{
|
||||
ptr_s->Close();
|
||||
delete ptr_s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ptr_s == NULL)
|
||||
{
|
||||
LOGINFO("WebAdmin: Accepted socket is NULL. Terminating WebAdmin to avoid busywait.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned ret;
|
||||
HANDLE hHandle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, Request, (void *)ptr_s, 0, &ret));
|
||||
CloseHandle(hHandle);
|
||||
#else
|
||||
// Mattes: TODO: this handle probably leaks!
|
||||
pthread_t * hHandle = new pthread_t;
|
||||
pthread_create( hHandle, NULL, Request, ptr_s);
|
||||
#endif
|
||||
}
|
||||
m_Events->Set();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
webserver::webserver(unsigned int port_to_listen, request_func r)
|
||||
{
|
||||
m_Socket = new SocketServer(port_to_listen, 1);
|
||||
|
||||
request_func_ = r;
|
||||
m_Events = new cEvents();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
webserver::~webserver()
|
||||
{
|
||||
delete m_Socket;
|
||||
delete m_Events;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
WebServer.cpp
|
||||
|
||||
Copyright (C) 2003-2007 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
Thanks to Tom Lynn who pointed out an error in this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
Note on point 2:
|
||||
THIS IS NOT THE ORIGINAL SOURCE1!!1!!!~!!~`1ONE!!`1
|
||||
*/
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include <ctime>
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
#include "WebServer.h"
|
||||
#include "cEvents.h"
|
||||
#include "Socket.h"
|
||||
#include "UrlHelper.h"
|
||||
#include "base64.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
webserver::request_func webserver::request_func_ = NULL;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static std::string EatLine( std::string& a_String )
|
||||
{
|
||||
std::string RetVal = "";
|
||||
unsigned int StringSize = a_String.size();
|
||||
const char* c = a_String.c_str();
|
||||
|
||||
for( unsigned int i = 0; i < StringSize; ++i, ++c)
|
||||
{
|
||||
if( *c == '\n' )
|
||||
{
|
||||
RetVal += *c;
|
||||
// ++i; ++c;
|
||||
// if( i < StringSize )
|
||||
// {
|
||||
// if( *c == '\r' )
|
||||
// {
|
||||
// RetVal += *c;
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
}
|
||||
RetVal += *c;
|
||||
}
|
||||
a_String = a_String.substr( RetVal.size() );
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Turns
|
||||
// "blabla my string with \"quotes\"!"
|
||||
// into
|
||||
// blabla my string with "quotes"!
|
||||
static std::string GetQuotedString( const std::string& a_String )
|
||||
{
|
||||
std::string RetVal;
|
||||
|
||||
bool bGotFirstQuote = false;
|
||||
bool bIgnoreNext = false;
|
||||
unsigned int StrLen = a_String.size();
|
||||
for( unsigned int i = 0; i < StrLen; ++i )
|
||||
{
|
||||
if( bIgnoreNext == false )
|
||||
{
|
||||
if( a_String[i] == '\"' )
|
||||
{
|
||||
if( bGotFirstQuote == false )
|
||||
{
|
||||
bGotFirstQuote = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if( a_String[i] == '\\' ) // Escape character
|
||||
{
|
||||
bIgnoreNext = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RetVal.push_back( a_String[i] );
|
||||
bIgnoreNext = false;
|
||||
}
|
||||
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ParseMultipartFormData( webserver::http_request& req, Socket* s)
|
||||
{
|
||||
static const std::string multipart_form_data = "multipart/form-data";
|
||||
if(req.content_type_.substr(0, multipart_form_data.size()) == multipart_form_data) // Difficult data... :(
|
||||
{
|
||||
AStringVector ContentTypeData = StringSplit( req.content_type_, "; " );
|
||||
|
||||
std::string boundary;
|
||||
// Find boundary
|
||||
for( unsigned int i = 0; i < ContentTypeData.size(); ++i )
|
||||
{
|
||||
static const std::string boundary_ = "boundary=";
|
||||
if( ContentTypeData[i].substr(0, boundary_.size()) == boundary_ ) // Found boundary
|
||||
{
|
||||
boundary = ContentTypeData[i].substr( boundary_.size() );
|
||||
}
|
||||
}
|
||||
|
||||
//LOGINFO("Boundary: %s", boundary.c_str() );
|
||||
std::string boundary_start = "--" + boundary;
|
||||
std::string boundary_end = boundary_start + "--";
|
||||
|
||||
std::string Content = s->ReceiveBytes( req.content_length_ );
|
||||
|
||||
//printf("Total content: \n%s\n", Content.c_str() );
|
||||
|
||||
// Should start with boundary!
|
||||
std::string line = EatLine( Content );
|
||||
if( line.substr(0, boundary_start.size() ) != boundary_start )
|
||||
{
|
||||
// Something was wrong! :(
|
||||
Content.clear();
|
||||
}
|
||||
|
||||
while( !Content.empty() )
|
||||
{
|
||||
webserver::formdata FormData;
|
||||
|
||||
static const std::string content_disposition = "Content-Disposition: ";
|
||||
static const std::string content_type = "Content-Type: ";
|
||||
|
||||
std::string f_disposition;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
std::string line = EatLine( Content );
|
||||
if( line.empty() )
|
||||
break;
|
||||
|
||||
unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d");
|
||||
if (pos_cr_lf == 0) break; // Empty line, indicates end of mime thingy
|
||||
|
||||
if( line.substr(0, content_disposition.size() ) == content_disposition )
|
||||
{
|
||||
f_disposition = line.substr(content_disposition.size());
|
||||
LOGINFO("Disposition: %s", f_disposition.c_str() );
|
||||
}
|
||||
else if( line.substr(0, content_type.size() ) == content_type )
|
||||
{
|
||||
FormData.content_type_ = line.substr(content_type.size());
|
||||
}
|
||||
|
||||
//LOGINFO("Got line: '%s'", line.c_str() );
|
||||
}
|
||||
|
||||
// Check if we got the proper headers
|
||||
if( !f_disposition.empty() )
|
||||
{
|
||||
static const std::string disp_name = "name=";
|
||||
static const std::string disp_filename = "filename=";
|
||||
|
||||
// Parse the disposition
|
||||
AStringVector DispositionData = StringSplit( f_disposition, "; " );
|
||||
for( unsigned int i = 0; i < DispositionData.size(); ++i )
|
||||
{
|
||||
if( DispositionData[i].substr(0, disp_name.size()) == disp_name )
|
||||
{
|
||||
FormData.name_ = GetQuotedString( DispositionData[i].substr(disp_name.size()) );
|
||||
}
|
||||
else if( DispositionData[i].substr(0, disp_filename.size()) == disp_filename )
|
||||
{
|
||||
FormData.filename_ = GetQuotedString( DispositionData[i].substr(disp_filename.size()) );
|
||||
}
|
||||
}
|
||||
|
||||
std::string ContentValue;
|
||||
// Parse until boundary_end is found
|
||||
while( 1 )
|
||||
{
|
||||
std::string line = EatLine( Content );
|
||||
if( line.empty() )
|
||||
break;
|
||||
|
||||
if( line.substr(0, boundary_end.size() ) == boundary_end )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( line.substr(0, boundary_start.size() ) == boundary_start )
|
||||
{
|
||||
break;
|
||||
}
|
||||
ContentValue.append( line.c_str(), line.size() );
|
||||
}
|
||||
|
||||
|
||||
FormData.value_ = ContentValue;
|
||||
}
|
||||
|
||||
req.multipart_formdata_.push_back( FormData );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned webserver::Request(void* ptr_s)
|
||||
#else
|
||||
void* webserver::Request(void* ptr_s)
|
||||
#endif
|
||||
{
|
||||
Socket* s = (reinterpret_cast<Socket*>(ptr_s));
|
||||
|
||||
std::string line = s->ReceiveLine();
|
||||
if (line.empty())
|
||||
{
|
||||
s->Close();
|
||||
delete s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_request req;
|
||||
std::string path;
|
||||
std::map<std::string, std::string> params;
|
||||
size_t posStartPath = 0;
|
||||
|
||||
if (line.find("GET") == 0)
|
||||
{
|
||||
req.method_="GET";
|
||||
posStartPath = line.find_first_not_of(" ",3);
|
||||
}
|
||||
else if (line.find("POST") == 0)
|
||||
{
|
||||
req.method_="POST";
|
||||
posStartPath = line.find_first_not_of(" ",4);
|
||||
}
|
||||
|
||||
SplitGetReq(line.substr(posStartPath), path, params);
|
||||
|
||||
req.status_ = "202 OK";
|
||||
req.s_ = s;
|
||||
req.path_ = path;
|
||||
req.params_ = params;
|
||||
|
||||
static const std::string authorization = "Authorization: Basic ";
|
||||
static const std::string accept = "Accept: " ;
|
||||
static const std::string accept_language = "Accept-Language: " ;
|
||||
static const std::string accept_encoding = "Accept-Encoding: " ;
|
||||
static const std::string user_agent = "User-Agent: " ;
|
||||
static const std::string content_length = "Content-Length: " ;
|
||||
static const std::string content_type = "Content-Type: " ;
|
||||
|
||||
while(1)
|
||||
{
|
||||
line=s->ReceiveLine();
|
||||
if (line.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d");
|
||||
if (pos_cr_lf == 0) break;
|
||||
|
||||
line = line.substr(0,pos_cr_lf);
|
||||
|
||||
if (line.substr(0, authorization.size()) == authorization)
|
||||
{
|
||||
req.authentication_given_ = true;
|
||||
std::string encoded = line.substr(authorization.size());
|
||||
std::string decoded = base64_decode(encoded);
|
||||
|
||||
unsigned int pos_colon = decoded.find(":");
|
||||
|
||||
req.username_ = decoded.substr(0, pos_colon);
|
||||
req.password_ = decoded.substr(pos_colon+1 );
|
||||
}
|
||||
else if (line.substr(0, accept.size()) == accept)
|
||||
{
|
||||
req.accept_ = line.substr(accept.size());
|
||||
}
|
||||
else if (line.substr(0, accept_language.size()) == accept_language)
|
||||
{
|
||||
req.accept_language_ = line.substr(accept_language.size());
|
||||
}
|
||||
else if (line.substr(0, accept_encoding.size()) == accept_encoding)
|
||||
{
|
||||
req.accept_encoding_ = line.substr(accept_encoding.size());
|
||||
}
|
||||
else if (line.substr(0, user_agent.size()) == user_agent)
|
||||
{
|
||||
req.user_agent_ = line.substr(user_agent.size());
|
||||
}
|
||||
else if (line.substr(0, content_length.size()) == content_length)
|
||||
{
|
||||
req.content_length_ = atoi( line.substr(content_length.size()).c_str() );
|
||||
}
|
||||
else if (line.substr(0, content_type.size()) == content_type)
|
||||
{
|
||||
req.content_type_ = line.substr(content_type.size());
|
||||
}
|
||||
}
|
||||
|
||||
if( (req.method_.compare("POST") == 0) && (req.content_length_ > 0) )
|
||||
{
|
||||
// The only content type we can parse at the moment, the default HTML post data
|
||||
if( req.content_type_.compare( "application/x-www-form-urlencoded" ) == 0 )
|
||||
{
|
||||
std::string Content = s->ReceiveBytes( req.content_length_ );
|
||||
Content.insert( 0, "/ ?" ); // Little hack, inserts dummy URL so that SplitGetReq() can work with this content
|
||||
|
||||
std::string dummy;
|
||||
std::map<std::string, std::string> post_params;
|
||||
SplitGetReq(Content, dummy, post_params);
|
||||
|
||||
req.params_post_ = post_params;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseMultipartFormData( req, s );
|
||||
}
|
||||
}
|
||||
|
||||
request_func_(&req);
|
||||
|
||||
std::stringstream str_str;
|
||||
str_str << req.answer_.size();
|
||||
|
||||
time_t ltime;
|
||||
time(<ime);
|
||||
tm* gmt= gmtime(<ime);
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::string const serverName = "MCServerWebAdmin (Windows)";
|
||||
#elif __APPLE__
|
||||
static std::string const serverName = "MCServerWebAdmin (MacOSX)";
|
||||
#else
|
||||
static std::string const serverName = "MCServerWebAdmin (Linux)";
|
||||
#endif
|
||||
|
||||
|
||||
char* asctime_remove_nl = std::asctime(gmt);
|
||||
asctime_remove_nl[24] = 0;
|
||||
|
||||
s->SendBytes("HTTP/1.1 ");
|
||||
|
||||
if (! req.auth_realm_.empty() )
|
||||
{
|
||||
s->SendLine("401 Unauthorized");
|
||||
s->SendBytes("WWW-Authenticate: Basic Realm=\"");
|
||||
s->SendBytes(req.auth_realm_);
|
||||
s->SendLine("\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
s->SendLine(req.status_);
|
||||
}
|
||||
s->SendLine(std::string("Date: ") + asctime_remove_nl + " GMT");
|
||||
s->SendLine(std::string("Server: ") +serverName);
|
||||
s->SendLine("Connection: close");
|
||||
s->SendLine("Content-Type: text/html; charset=ISO-8859-1");
|
||||
s->SendLine("Content-Length: " + str_str.str());
|
||||
s->SendLine("");
|
||||
s->SendLine(req.answer_);
|
||||
|
||||
s->Close( true ); // true = wait for all data to be sent before closing
|
||||
delete s;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void webserver::Stop()
|
||||
{
|
||||
m_bStop = true;
|
||||
m_Socket->Close();
|
||||
m_Events->Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool webserver::Begin()
|
||||
{
|
||||
if (!m_Socket->IsValid())
|
||||
{
|
||||
LOGINFO("WebAdmin: The server socket is invalid. Terminating WebAdmin.");
|
||||
return false;
|
||||
}
|
||||
m_bStop = false;
|
||||
while ( !m_bStop )
|
||||
{
|
||||
Socket * ptr_s = m_Socket->Accept();
|
||||
if (m_bStop)
|
||||
{
|
||||
if (ptr_s != 0)
|
||||
{
|
||||
ptr_s->Close();
|
||||
delete ptr_s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ptr_s == NULL)
|
||||
{
|
||||
LOGINFO("WebAdmin: Accepted socket is NULL. Terminating WebAdmin to avoid busywait.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned ret;
|
||||
HANDLE hHandle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, Request, (void *)ptr_s, 0, &ret));
|
||||
CloseHandle(hHandle);
|
||||
#else
|
||||
// Mattes: TODO: this handle probably leaks!
|
||||
pthread_t * hHandle = new pthread_t;
|
||||
pthread_create( hHandle, NULL, Request, ptr_s);
|
||||
#endif
|
||||
}
|
||||
m_Events->Set();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
webserver::webserver(unsigned int port_to_listen, request_func r)
|
||||
{
|
||||
m_Socket = new SocketServer(port_to_listen, 1);
|
||||
|
||||
request_func_ = r;
|
||||
m_Events = new cEvents();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
webserver::~webserver()
|
||||
{
|
||||
delete m_Socket;
|
||||
delete m_Events;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "base64.h"
|
||||
#include <iostream>
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
|
||||
static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(i = 0; (i <4) ; i++)
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++)
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string const& encoded_string) {
|
||||
int in_len = encoded_string.size();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
std::string ret;
|
||||
|
||||
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||
if (i ==4) {
|
||||
for (i = 0; i <4; i++)
|
||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++)
|
||||
ret += char_array_3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j <4; j++)
|
||||
char_array_4[j] = 0;
|
||||
|
||||
for (j = 0; j <4; j++)
|
||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "base64.h"
|
||||
#include <iostream>
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
|
||||
static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(i = 0; (i <4) ; i++)
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++)
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string const& encoded_string) {
|
||||
int in_len = encoded_string.size();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
std::string ret;
|
||||
|
||||
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||
if (i ==4) {
|
||||
for (i = 0; i <4; i++)
|
||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++)
|
||||
ret += char_array_3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j <4; j++)
|
||||
char_array_4[j] = 0;
|
||||
|
||||
for (j = 0; j <4; j++)
|
||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,125 +1,125 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "cEvents.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cEvents::cEvents( 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 cEvents: 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, "cEvents%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 cEvents. (%i)", errno);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cEvents::~cEvents()
|
||||
{
|
||||
#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, "cEvents%p", &HandlePtr );
|
||||
// LOG("Closing event: %s", c_Str );
|
||||
// LOG("Sem ptr: %p", HandlePtr );
|
||||
if( sem_close( HandlePtr ) != 0 )
|
||||
{
|
||||
LOG("ERROR: Could not close cEvents. (%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 cEvents::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 cEvents. (%i)", errno);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cEvents::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 cEvents. (%i)", errno);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "cEvents.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cEvents::cEvents( 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 cEvents: 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, "cEvents%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 cEvents. (%i)", errno);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cEvents::~cEvents()
|
||||
{
|
||||
#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, "cEvents%p", &HandlePtr );
|
||||
// LOG("Closing event: %s", c_Str );
|
||||
// LOG("Sem ptr: %p", HandlePtr );
|
||||
if( sem_close( HandlePtr ) != 0 )
|
||||
{
|
||||
LOG("ERROR: Could not close cEvents. (%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 cEvents::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 cEvents. (%i)", errno);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cEvents::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 cEvents. (%i)", errno);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +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
|
||||
#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
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
inline bool fOpenFile( FILE*& a_hFile, const char* a_FileName, const char* a_Mode )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return fopen_s(&a_hFile, a_FileName, a_Mode ) == 0;
|
||||
#else
|
||||
return (a_hFile = fopen(a_FileName, a_Mode )) != 0;
|
||||
#ifdef _WIN32
|
||||
return fopen_s(&a_hFile, a_FileName, a_Mode ) == 0;
|
||||
#else
|
||||
return (a_hFile = fopen(a_FileName, a_Mode )) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +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(a_Directory, &Attrib);
|
||||
#else
|
||||
mkdir(a_Directory, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
#endif
|
||||
#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(a_Directory, &Attrib);
|
||||
#else
|
||||
mkdir(a_Directory, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
#endif
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class cMakeDir
|
||||
{
|
||||
public:
|
||||
static void MakeDir( const char* a_Directory );
|
||||
#pragma once
|
||||
|
||||
class cMakeDir
|
||||
{
|
||||
public:
|
||||
static void MakeDir( const char* a_Directory );
|
||||
};
|
||||
@@ -14,8 +14,8 @@
|
||||
int main ()
|
||||
{
|
||||
|
||||
#ifdef _DEBUG
|
||||
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
|
||||
#ifdef _DEBUG
|
||||
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
|
||||
#endif
|
||||
|
||||
cTimer Timer;
|
||||
@@ -63,8 +63,8 @@ int main ()
|
||||
clock_t progEnd = clock(); //end main program timer
|
||||
std::cout << "Time to complete converter: " << double(Timer.diffclock(progEnd,progBegin)) << " Seconds"<< std::endl;
|
||||
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks();
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
1212
iniFile/iniFile.cpp
1212
iniFile/iniFile.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,186 +1,186 @@
|
||||
// IniFile.cpp: Implementation of the CIniFile class.
|
||||
// Written by: Adam Clauss
|
||||
// Email: cabadam@tamu.edu
|
||||
// You may use this class/code as you wish in your programs. Feel free to distribute it, and
|
||||
// email suggested changes to me.
|
||||
//
|
||||
// Rewritten by: Shane Hill
|
||||
// Date: 21/08/2001
|
||||
// Email: Shane.Hill@dsto.defence.gov.au
|
||||
// Reason: Remove dependancy on MFC. Code should compile on any
|
||||
// platform. Tested on Windows/Linux/Irix
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
!! MODIFIED BY FAKETRUTH and madmaxoft!!
|
||||
*/
|
||||
|
||||
#ifndef CIniFile_H
|
||||
#define CIniFile_H
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define MAX_KEYNAME 128
|
||||
#define MAX_VALUENAME 128
|
||||
#define MAX_VALUEDATA 2048
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cIniFile //tolua_export
|
||||
{ //tolua_export
|
||||
private:
|
||||
bool caseInsensitive;
|
||||
std::string path;
|
||||
struct key {
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> comments;
|
||||
};
|
||||
std::vector<key> keys;
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::string> comments;
|
||||
std::string CheckCase( std::string s) const;
|
||||
|
||||
public:
|
||||
enum errors{ noID = -1}; //tolua_export
|
||||
cIniFile( const std::string iniPath = ""); //tolua_export
|
||||
virtual ~cIniFile() {}
|
||||
|
||||
// Sets whether or not keynames and valuenames should be case sensitive.
|
||||
// The default is case insensitive.
|
||||
void CaseSensitive() {caseInsensitive = false;} //tolua_export
|
||||
void CaseInsensitive() {caseInsensitive = true;} //tolua_export
|
||||
|
||||
// Sets path of ini file to read and write from.
|
||||
void Path(const std::string & newPath) {path = newPath;} //tolua_export
|
||||
std::string Path() const {return path;} //tolua_export
|
||||
void SetPath(const std::string & newPath) {Path( newPath);} //tolua_export
|
||||
|
||||
// Reads ini file specified using path.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool ReadFile(); //tolua_export
|
||||
|
||||
// Writes data stored in class to ini file.
|
||||
bool WriteFile(); //tolua_export
|
||||
|
||||
// Deletes all stored ini data.
|
||||
void Erase(); //tolua_export
|
||||
void Clear() {Erase();} //tolua_export
|
||||
void Reset() {Erase();} //tolua_export
|
||||
|
||||
// Returns index of specified key, or noID if not found.
|
||||
long FindKey( const std::string & keyname) const; //tolua_export
|
||||
|
||||
// Returns index of specified value, in the specified key, or noID if not found.
|
||||
long FindValue( const unsigned keyID, const std::string & valuename) const; //tolua_export
|
||||
|
||||
// Returns number of keys currently in the ini.
|
||||
unsigned NumKeys() const {return names.size();} //tolua_export
|
||||
unsigned GetNumKeys() const {return NumKeys();} //tolua_export
|
||||
|
||||
// Add a key name.
|
||||
unsigned AddKeyName( const std::string & keyname); //tolua_export
|
||||
|
||||
// Returns key names by index.
|
||||
std::string KeyName( const unsigned keyID) const; //tolua_export
|
||||
std::string GetKeyName( const unsigned keyID) const {return KeyName(keyID);} //tolua_export
|
||||
|
||||
// Returns number of values stored for specified key.
|
||||
unsigned NumValues( const std::string & keyname); //tolua_export
|
||||
unsigned GetNumValues( const std::string & keyname) {return NumValues( keyname);} //tolua_export
|
||||
unsigned NumValues( const unsigned keyID); //tolua_export
|
||||
unsigned GetNumValues( const unsigned keyID) {return NumValues( keyID);} //tolua_export
|
||||
|
||||
// Returns value name by index for a given keyname or keyID.
|
||||
std::string ValueName( const std::string & keyname, const unsigned valueID) const; //tolua_export
|
||||
std::string GetValueName( const std::string & keyname, const unsigned valueID) const { //tolua_export
|
||||
return ValueName( keyname, valueID);
|
||||
} //tolua_export
|
||||
std::string ValueName( const unsigned keyID, const unsigned valueID) const; //tolua_export
|
||||
std::string GetValueName( const unsigned keyID, const unsigned valueID) const { //tolua_export
|
||||
return ValueName( keyID, valueID);
|
||||
} //tolua_export
|
||||
|
||||
// Gets value of [keyname] valuename =.
|
||||
// Overloaded to return string, int, and double.
|
||||
// Returns defValue if key/value not found.
|
||||
AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const; // tolua_export
|
||||
AString GetValue (const unsigned keyID, const unsigned valueID, const AString & defValue = "") const; // tolua_export
|
||||
double GetValueF(const AString & keyname, const AString & valuename, const double defValue = 0) const; // tolua_export
|
||||
int GetValueI(const AString & keyname, const AString & valuename, const int defValue = 0) const; // tolua_export
|
||||
bool GetValueB(const AString & keyname, const AString & valuename, const bool defValue = false) const { // tolua_export
|
||||
return ( GetValueI( keyname, valuename, int( defValue)) > 0);
|
||||
} // tolua_export
|
||||
|
||||
// Gets the value; if not found, write the default to the INI file
|
||||
AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = ""); // tolua_export
|
||||
double GetValueSetF(const AString & keyname, const AString & valuename, const double defValue = 0.0); // tolua_export
|
||||
int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0); // tolua_export
|
||||
bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) { // tolua_export
|
||||
return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) > 0);
|
||||
} // tolua_export
|
||||
|
||||
// Sets value of [keyname] valuename =.
|
||||
// Specify the optional paramter as false (0) if you do not want it to create
|
||||
// the key if it doesn't exist. Returns true if data entered, false otherwise.
|
||||
// Overloaded to accept string, int, and double.
|
||||
bool SetValue( const unsigned keyID, const unsigned valueID, const std::string & value); //tolua_export
|
||||
bool SetValue( const std::string & keyname, const std::string & valuename, const std::string & value, const bool create = true); //tolua_export
|
||||
bool SetValueI( const std::string & keyname, const std::string & valuename, const int value, const bool create = true); //tolua_export
|
||||
bool SetValueB( const std::string & keyname, const std::string & valuename, const bool value, const bool create = true) { //tolua_export
|
||||
return SetValueI( keyname, valuename, int(value), create);
|
||||
} //tolua_export
|
||||
bool SetValueF( const std::string & keyname, const std::string & valuename, const double value, const bool create = true); //tolua_export
|
||||
bool SetValueV( const std::string & keyname, const std::string & valuename, char *format, ...);
|
||||
|
||||
// Deletes specified value.
|
||||
// Returns true if value existed and deleted, false otherwise.
|
||||
bool DeleteValueByID( const unsigned keyID, const unsigned valueID ); //tolua_export
|
||||
bool DeleteValue( const std::string & keyname, const std::string & valuename); //tolua_export
|
||||
|
||||
// Deletes specified key and all values contained within.
|
||||
// Returns true if key existed and deleted, false otherwise.
|
||||
bool DeleteKey(const std::string & keyname); //tolua_export
|
||||
|
||||
// Header comment functions.
|
||||
// Header comments are those comments before the first key.
|
||||
//
|
||||
// Number of header comments.
|
||||
unsigned NumHeaderComments() {return comments.size();} //tolua_export
|
||||
// Add a header comment.
|
||||
void HeaderComment( const std::string & comment); //tolua_export
|
||||
// Return a header comment.
|
||||
std::string HeaderComment( const unsigned commentID) const; //tolua_export
|
||||
// Delete a header comment.
|
||||
bool DeleteHeaderComment( unsigned commentID); //tolua_export
|
||||
// Delete all header comments.
|
||||
void DeleteHeaderComments() {comments.clear();} //tolua_export
|
||||
|
||||
// Key comment functions.
|
||||
// Key comments are those comments within a key. Any comments
|
||||
// defined within value names will be added to this list. Therefore,
|
||||
// these comments will be moved to the top of the key definition when
|
||||
// the CIniFile::WriteFile() is called.
|
||||
//
|
||||
// Number of key comments.
|
||||
unsigned NumKeyComments( const unsigned keyID) const; //tolua_export
|
||||
unsigned NumKeyComments( const std::string & keyname) const; //tolua_export
|
||||
// Add a key comment.
|
||||
bool KeyComment( const unsigned keyID, const std::string & comment); //tolua_export
|
||||
bool KeyComment( const std::string & keyname, const std::string & comment); //tolua_export
|
||||
// Return a key comment.
|
||||
std::string KeyComment( const unsigned keyID, const unsigned commentID) const; //tolua_export
|
||||
std::string KeyComment( const std::string & keyname, const unsigned commentID) const; //tolua_export
|
||||
// Delete a key comment.
|
||||
bool DeleteKeyComment( const unsigned keyID, const unsigned commentID); //tolua_export
|
||||
bool DeleteKeyComment( const std::string & keyname, const unsigned commentID); //tolua_export
|
||||
// Delete all comments for a key.
|
||||
bool DeleteKeyComments( const unsigned keyID); //tolua_export
|
||||
bool DeleteKeyComments( const std::string & keyname); //tolua_export
|
||||
}; //tolua_export
|
||||
|
||||
#endif
|
||||
// IniFile.cpp: Implementation of the CIniFile class.
|
||||
// Written by: Adam Clauss
|
||||
// Email: cabadam@tamu.edu
|
||||
// You may use this class/code as you wish in your programs. Feel free to distribute it, and
|
||||
// email suggested changes to me.
|
||||
//
|
||||
// Rewritten by: Shane Hill
|
||||
// Date: 21/08/2001
|
||||
// Email: Shane.Hill@dsto.defence.gov.au
|
||||
// Reason: Remove dependancy on MFC. Code should compile on any
|
||||
// platform. Tested on Windows/Linux/Irix
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
!! MODIFIED BY FAKETRUTH and madmaxoft!!
|
||||
*/
|
||||
|
||||
#ifndef CIniFile_H
|
||||
#define CIniFile_H
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define MAX_KEYNAME 128
|
||||
#define MAX_VALUENAME 128
|
||||
#define MAX_VALUEDATA 2048
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cIniFile //tolua_export
|
||||
{ //tolua_export
|
||||
private:
|
||||
bool caseInsensitive;
|
||||
std::string path;
|
||||
struct key {
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> comments;
|
||||
};
|
||||
std::vector<key> keys;
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::string> comments;
|
||||
std::string CheckCase( std::string s) const;
|
||||
|
||||
public:
|
||||
enum errors{ noID = -1}; //tolua_export
|
||||
cIniFile( const std::string iniPath = ""); //tolua_export
|
||||
virtual ~cIniFile() {}
|
||||
|
||||
// Sets whether or not keynames and valuenames should be case sensitive.
|
||||
// The default is case insensitive.
|
||||
void CaseSensitive() {caseInsensitive = false;} //tolua_export
|
||||
void CaseInsensitive() {caseInsensitive = true;} //tolua_export
|
||||
|
||||
// Sets path of ini file to read and write from.
|
||||
void Path(const std::string & newPath) {path = newPath;} //tolua_export
|
||||
std::string Path() const {return path;} //tolua_export
|
||||
void SetPath(const std::string & newPath) {Path( newPath);} //tolua_export
|
||||
|
||||
// Reads ini file specified using path.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool ReadFile(); //tolua_export
|
||||
|
||||
// Writes data stored in class to ini file.
|
||||
bool WriteFile(); //tolua_export
|
||||
|
||||
// Deletes all stored ini data.
|
||||
void Erase(); //tolua_export
|
||||
void Clear() {Erase();} //tolua_export
|
||||
void Reset() {Erase();} //tolua_export
|
||||
|
||||
// Returns index of specified key, or noID if not found.
|
||||
long FindKey( const std::string & keyname) const; //tolua_export
|
||||
|
||||
// Returns index of specified value, in the specified key, or noID if not found.
|
||||
long FindValue( const unsigned keyID, const std::string & valuename) const; //tolua_export
|
||||
|
||||
// Returns number of keys currently in the ini.
|
||||
unsigned NumKeys() const {return names.size();} //tolua_export
|
||||
unsigned GetNumKeys() const {return NumKeys();} //tolua_export
|
||||
|
||||
// Add a key name.
|
||||
unsigned AddKeyName( const std::string & keyname); //tolua_export
|
||||
|
||||
// Returns key names by index.
|
||||
std::string KeyName( const unsigned keyID) const; //tolua_export
|
||||
std::string GetKeyName( const unsigned keyID) const {return KeyName(keyID);} //tolua_export
|
||||
|
||||
// Returns number of values stored for specified key.
|
||||
unsigned NumValues( const std::string & keyname); //tolua_export
|
||||
unsigned GetNumValues( const std::string & keyname) {return NumValues( keyname);} //tolua_export
|
||||
unsigned NumValues( const unsigned keyID); //tolua_export
|
||||
unsigned GetNumValues( const unsigned keyID) {return NumValues( keyID);} //tolua_export
|
||||
|
||||
// Returns value name by index for a given keyname or keyID.
|
||||
std::string ValueName( const std::string & keyname, const unsigned valueID) const; //tolua_export
|
||||
std::string GetValueName( const std::string & keyname, const unsigned valueID) const { //tolua_export
|
||||
return ValueName( keyname, valueID);
|
||||
} //tolua_export
|
||||
std::string ValueName( const unsigned keyID, const unsigned valueID) const; //tolua_export
|
||||
std::string GetValueName( const unsigned keyID, const unsigned valueID) const { //tolua_export
|
||||
return ValueName( keyID, valueID);
|
||||
} //tolua_export
|
||||
|
||||
// Gets value of [keyname] valuename =.
|
||||
// Overloaded to return string, int, and double.
|
||||
// Returns defValue if key/value not found.
|
||||
AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const; // tolua_export
|
||||
AString GetValue (const unsigned keyID, const unsigned valueID, const AString & defValue = "") const; // tolua_export
|
||||
double GetValueF(const AString & keyname, const AString & valuename, const double defValue = 0) const; // tolua_export
|
||||
int GetValueI(const AString & keyname, const AString & valuename, const int defValue = 0) const; // tolua_export
|
||||
bool GetValueB(const AString & keyname, const AString & valuename, const bool defValue = false) const { // tolua_export
|
||||
return ( GetValueI( keyname, valuename, int( defValue)) > 0);
|
||||
} // tolua_export
|
||||
|
||||
// Gets the value; if not found, write the default to the INI file
|
||||
AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = ""); // tolua_export
|
||||
double GetValueSetF(const AString & keyname, const AString & valuename, const double defValue = 0.0); // tolua_export
|
||||
int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0); // tolua_export
|
||||
bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) { // tolua_export
|
||||
return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) > 0);
|
||||
} // tolua_export
|
||||
|
||||
// Sets value of [keyname] valuename =.
|
||||
// Specify the optional paramter as false (0) if you do not want it to create
|
||||
// the key if it doesn't exist. Returns true if data entered, false otherwise.
|
||||
// Overloaded to accept string, int, and double.
|
||||
bool SetValue( const unsigned keyID, const unsigned valueID, const std::string & value); //tolua_export
|
||||
bool SetValue( const std::string & keyname, const std::string & valuename, const std::string & value, const bool create = true); //tolua_export
|
||||
bool SetValueI( const std::string & keyname, const std::string & valuename, const int value, const bool create = true); //tolua_export
|
||||
bool SetValueB( const std::string & keyname, const std::string & valuename, const bool value, const bool create = true) { //tolua_export
|
||||
return SetValueI( keyname, valuename, int(value), create);
|
||||
} //tolua_export
|
||||
bool SetValueF( const std::string & keyname, const std::string & valuename, const double value, const bool create = true); //tolua_export
|
||||
bool SetValueV( const std::string & keyname, const std::string & valuename, char *format, ...);
|
||||
|
||||
// Deletes specified value.
|
||||
// Returns true if value existed and deleted, false otherwise.
|
||||
bool DeleteValueByID( const unsigned keyID, const unsigned valueID ); //tolua_export
|
||||
bool DeleteValue( const std::string & keyname, const std::string & valuename); //tolua_export
|
||||
|
||||
// Deletes specified key and all values contained within.
|
||||
// Returns true if key existed and deleted, false otherwise.
|
||||
bool DeleteKey(const std::string & keyname); //tolua_export
|
||||
|
||||
// Header comment functions.
|
||||
// Header comments are those comments before the first key.
|
||||
//
|
||||
// Number of header comments.
|
||||
unsigned NumHeaderComments() {return comments.size();} //tolua_export
|
||||
// Add a header comment.
|
||||
void HeaderComment( const std::string & comment); //tolua_export
|
||||
// Return a header comment.
|
||||
std::string HeaderComment( const unsigned commentID) const; //tolua_export
|
||||
// Delete a header comment.
|
||||
bool DeleteHeaderComment( unsigned commentID); //tolua_export
|
||||
// Delete all header comments.
|
||||
void DeleteHeaderComments() {comments.clear();} //tolua_export
|
||||
|
||||
// Key comment functions.
|
||||
// Key comments are those comments within a key. Any comments
|
||||
// defined within value names will be added to this list. Therefore,
|
||||
// these comments will be moved to the top of the key definition when
|
||||
// the CIniFile::WriteFile() is called.
|
||||
//
|
||||
// Number of key comments.
|
||||
unsigned NumKeyComments( const unsigned keyID) const; //tolua_export
|
||||
unsigned NumKeyComments( const std::string & keyname) const; //tolua_export
|
||||
// Add a key comment.
|
||||
bool KeyComment( const unsigned keyID, const std::string & comment); //tolua_export
|
||||
bool KeyComment( const std::string & keyname, const std::string & comment); //tolua_export
|
||||
// Return a key comment.
|
||||
std::string KeyComment( const unsigned keyID, const unsigned commentID) const; //tolua_export
|
||||
std::string KeyComment( const std::string & keyname, const unsigned commentID) const; //tolua_export
|
||||
// Delete a key comment.
|
||||
bool DeleteKeyComment( const unsigned keyID, const unsigned commentID); //tolua_export
|
||||
bool DeleteKeyComment( const std::string & keyname, const unsigned commentID); //tolua_export
|
||||
// Delete all comments for a key.
|
||||
bool DeleteKeyComments( const unsigned keyID); //tolua_export
|
||||
bool DeleteKeyComments( const std::string & keyname); //tolua_export
|
||||
}; //tolua_export
|
||||
|
||||
#endif
|
||||
|
||||
42674
source/Bindings.cpp
42674
source/Bindings.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
** Lua binding: AllToLua
|
||||
** Generated automatically by tolua++-1.0.92 on Thu Jun 14 14:20:17 2012.
|
||||
*/
|
||||
|
||||
/* Exported function */
|
||||
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
|
||||
|
||||
/*
|
||||
** Lua binding: AllToLua
|
||||
** Generated automatically by tolua++-1.0.92 on Thu Jun 14 14:20:17 2012.
|
||||
*/
|
||||
|
||||
/* Exported function */
|
||||
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
|
||||
|
||||
|
||||
@@ -1,293 +1,293 @@
|
||||
|
||||
// BioGen.cpp
|
||||
|
||||
// Implements the various biome generators
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BioGen.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenConstant:
|
||||
|
||||
void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
|
||||
{
|
||||
a_BiomeMap[i] = m_Biome;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenCache:
|
||||
|
||||
cBioGenCache::cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize) :
|
||||
m_BioGenToCache(a_BioGenToCache),
|
||||
m_CacheSize(a_CacheSize),
|
||||
m_CacheOrder(new int[a_CacheSize]),
|
||||
m_CacheData(new sCacheData[a_CacheSize]),
|
||||
m_NumHits(0),
|
||||
m_NumMisses(0),
|
||||
m_TotalChain(0)
|
||||
{
|
||||
for (int i = 0; i < m_CacheSize; i++)
|
||||
{
|
||||
m_CacheOrder[i] = i;
|
||||
m_CacheData[i].m_ChunkX = 0x7fffffff;
|
||||
m_CacheData[i].m_ChunkZ = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBioGenCache::~cBioGenCache()
|
||||
{
|
||||
delete m_CacheData;
|
||||
delete m_CacheOrder;
|
||||
delete m_BioGenToCache;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
if (((m_NumHits + m_NumMisses) % 1024) == 10)
|
||||
{
|
||||
LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
|
||||
LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_CacheSize; i++)
|
||||
{
|
||||
if (
|
||||
(m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) ||
|
||||
(m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Found it in the cache
|
||||
int Idx = m_CacheOrder[i];
|
||||
|
||||
// Move to front:
|
||||
for (int j = i; j > 0; j--)
|
||||
{
|
||||
m_CacheOrder[j] = m_CacheOrder[j - 1];
|
||||
}
|
||||
m_CacheOrder[0] = Idx;
|
||||
|
||||
// Use the cached data:
|
||||
memcpy(a_BiomeMap, m_CacheData[Idx].m_BiomeMap, sizeof(a_BiomeMap));
|
||||
|
||||
m_NumHits++;
|
||||
m_TotalChain += i;
|
||||
return;
|
||||
} // for i - cache
|
||||
|
||||
// Not in the cache:
|
||||
m_NumMisses++;
|
||||
m_BioGenToCache->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
|
||||
|
||||
// Insert it as the first item in the MRU order:
|
||||
int Idx = m_CacheOrder[m_CacheSize - 1];
|
||||
for (int i = m_CacheSize - 1; i > 0; i--)
|
||||
{
|
||||
m_CacheOrder[i] = m_CacheOrder[i - 1];
|
||||
} // for i - m_CacheOrder[]
|
||||
m_CacheOrder[0] = Idx;
|
||||
memcpy(m_CacheData[Idx].m_BiomeMap, a_BiomeMap, sizeof(a_BiomeMap));
|
||||
m_CacheData[Idx].m_ChunkX = a_ChunkX;
|
||||
m_CacheData[Idx].m_ChunkZ = a_ChunkZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBiomeGenList:
|
||||
|
||||
void cBiomeGenList::InitializeBiomes(const AString & a_Biomes)
|
||||
{
|
||||
AStringVector Split = StringSplit(a_Biomes, ",");
|
||||
|
||||
// Convert each string in the list into biome:
|
||||
for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr)
|
||||
{
|
||||
EMCSBiome Biome = StringToBiome(*itr);
|
||||
if (Biome != -1)
|
||||
{
|
||||
m_Biomes.push_back(Biome);
|
||||
}
|
||||
} // for itr - Split[]
|
||||
if (!m_Biomes.empty())
|
||||
{
|
||||
m_BiomesCount = (int)m_Biomes.size();
|
||||
return;
|
||||
}
|
||||
|
||||
// There were no biomes, add default biomes:
|
||||
static EMCSBiome Biomes[] =
|
||||
{
|
||||
biOcean,
|
||||
biPlains,
|
||||
biDesert,
|
||||
biExtremeHills,
|
||||
biForest,
|
||||
biTaiga,
|
||||
biSwampland,
|
||||
biRiver,
|
||||
biFrozenOcean,
|
||||
biFrozenRiver,
|
||||
biIcePlains,
|
||||
biIceMountains,
|
||||
biMushroomIsland,
|
||||
biMushroomShore,
|
||||
biBeach,
|
||||
biDesertHills,
|
||||
biForestHills,
|
||||
biTaigaHills,
|
||||
biExtremeHillsEdge,
|
||||
biJungle,
|
||||
biJungleHills,
|
||||
} ;
|
||||
m_Biomes.reserve(ARRAYCOUNT(Biomes));
|
||||
for (int i = 0; i < ARRAYCOUNT(Biomes); i++)
|
||||
{
|
||||
m_Biomes.push_back(Biomes[i]);
|
||||
}
|
||||
m_BiomesCount = (int)m_Biomes.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenCheckerboard:
|
||||
|
||||
void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
int Base = cChunkDef::Width * a_ChunkZ + z;
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Add = cChunkDef::Width * a_ChunkX + x;
|
||||
a_BiomeMap[x + cChunkDef::Width * z] = m_Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % m_BiomesCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenVoronoi :
|
||||
|
||||
void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
int BaseZ = cChunkDef::Width * a_ChunkZ;
|
||||
int BaseX = cChunkDef::Width * a_ChunkX;
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
int AbsoluteZ = BaseZ + z;
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ));
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ)
|
||||
{
|
||||
int CellX = a_BlockX / m_CellSize;
|
||||
int CellZ = a_BlockZ / m_CellSize;
|
||||
|
||||
// Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution
|
||||
|
||||
// Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell
|
||||
double MinDist = m_CellSize * m_CellSize; // There has to be a cell closer than this
|
||||
EMCSBiome res = biPlains; // Will be overriden
|
||||
for (int x = CellX - 2; x <= CellX + 2; x++)
|
||||
{
|
||||
int BaseX = x * m_CellSize;
|
||||
for (int z = CellZ - 2; z < CellZ + 2; z++)
|
||||
{
|
||||
int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize;
|
||||
int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize;
|
||||
int SeedX = BaseX + OffsetX;
|
||||
int SeedZ = z * m_CellSize + OffsetZ;
|
||||
|
||||
double Dist = sqrt((double)((SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ)));
|
||||
if (Dist < MinDist)
|
||||
{
|
||||
MinDist = Dist;
|
||||
res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount];
|
||||
}
|
||||
} // for z
|
||||
} // for x
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenDistortedVoronoi:
|
||||
|
||||
void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
int BaseZ = cChunkDef::Width * a_ChunkZ;
|
||||
int BaseX = cChunkDef::Width * a_ChunkX;
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
int AbsoluteZ = BaseZ + z;
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int DistX, DistZ;
|
||||
Distort(BaseX + x, AbsoluteZ, DistX, DistZ);
|
||||
cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistX, DistZ));
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBioGenDistortedVoronoi::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ)
|
||||
{
|
||||
double NoiseX = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 1000);
|
||||
NoiseX += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 2000);
|
||||
NoiseX += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 3000);
|
||||
double NoiseZ = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 4000);
|
||||
NoiseZ += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 5000);
|
||||
NoiseZ += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 6000);
|
||||
|
||||
a_DistortedX = a_BlockX + (int)(m_CellSize * 0.5 * NoiseX);
|
||||
a_DistortedZ = a_BlockZ + (int)(m_CellSize * 0.5 * NoiseZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BioGen.cpp
|
||||
|
||||
// Implements the various biome generators
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BioGen.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenConstant:
|
||||
|
||||
void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
|
||||
{
|
||||
a_BiomeMap[i] = m_Biome;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenCache:
|
||||
|
||||
cBioGenCache::cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize) :
|
||||
m_BioGenToCache(a_BioGenToCache),
|
||||
m_CacheSize(a_CacheSize),
|
||||
m_CacheOrder(new int[a_CacheSize]),
|
||||
m_CacheData(new sCacheData[a_CacheSize]),
|
||||
m_NumHits(0),
|
||||
m_NumMisses(0),
|
||||
m_TotalChain(0)
|
||||
{
|
||||
for (int i = 0; i < m_CacheSize; i++)
|
||||
{
|
||||
m_CacheOrder[i] = i;
|
||||
m_CacheData[i].m_ChunkX = 0x7fffffff;
|
||||
m_CacheData[i].m_ChunkZ = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBioGenCache::~cBioGenCache()
|
||||
{
|
||||
delete m_CacheData;
|
||||
delete m_CacheOrder;
|
||||
delete m_BioGenToCache;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
if (((m_NumHits + m_NumMisses) % 1024) == 10)
|
||||
{
|
||||
LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
|
||||
LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_CacheSize; i++)
|
||||
{
|
||||
if (
|
||||
(m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) ||
|
||||
(m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Found it in the cache
|
||||
int Idx = m_CacheOrder[i];
|
||||
|
||||
// Move to front:
|
||||
for (int j = i; j > 0; j--)
|
||||
{
|
||||
m_CacheOrder[j] = m_CacheOrder[j - 1];
|
||||
}
|
||||
m_CacheOrder[0] = Idx;
|
||||
|
||||
// Use the cached data:
|
||||
memcpy(a_BiomeMap, m_CacheData[Idx].m_BiomeMap, sizeof(a_BiomeMap));
|
||||
|
||||
m_NumHits++;
|
||||
m_TotalChain += i;
|
||||
return;
|
||||
} // for i - cache
|
||||
|
||||
// Not in the cache:
|
||||
m_NumMisses++;
|
||||
m_BioGenToCache->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
|
||||
|
||||
// Insert it as the first item in the MRU order:
|
||||
int Idx = m_CacheOrder[m_CacheSize - 1];
|
||||
for (int i = m_CacheSize - 1; i > 0; i--)
|
||||
{
|
||||
m_CacheOrder[i] = m_CacheOrder[i - 1];
|
||||
} // for i - m_CacheOrder[]
|
||||
m_CacheOrder[0] = Idx;
|
||||
memcpy(m_CacheData[Idx].m_BiomeMap, a_BiomeMap, sizeof(a_BiomeMap));
|
||||
m_CacheData[Idx].m_ChunkX = a_ChunkX;
|
||||
m_CacheData[Idx].m_ChunkZ = a_ChunkZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBiomeGenList:
|
||||
|
||||
void cBiomeGenList::InitializeBiomes(const AString & a_Biomes)
|
||||
{
|
||||
AStringVector Split = StringSplit(a_Biomes, ",");
|
||||
|
||||
// Convert each string in the list into biome:
|
||||
for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr)
|
||||
{
|
||||
EMCSBiome Biome = StringToBiome(*itr);
|
||||
if (Biome != -1)
|
||||
{
|
||||
m_Biomes.push_back(Biome);
|
||||
}
|
||||
} // for itr - Split[]
|
||||
if (!m_Biomes.empty())
|
||||
{
|
||||
m_BiomesCount = (int)m_Biomes.size();
|
||||
return;
|
||||
}
|
||||
|
||||
// There were no biomes, add default biomes:
|
||||
static EMCSBiome Biomes[] =
|
||||
{
|
||||
biOcean,
|
||||
biPlains,
|
||||
biDesert,
|
||||
biExtremeHills,
|
||||
biForest,
|
||||
biTaiga,
|
||||
biSwampland,
|
||||
biRiver,
|
||||
biFrozenOcean,
|
||||
biFrozenRiver,
|
||||
biIcePlains,
|
||||
biIceMountains,
|
||||
biMushroomIsland,
|
||||
biMushroomShore,
|
||||
biBeach,
|
||||
biDesertHills,
|
||||
biForestHills,
|
||||
biTaigaHills,
|
||||
biExtremeHillsEdge,
|
||||
biJungle,
|
||||
biJungleHills,
|
||||
} ;
|
||||
m_Biomes.reserve(ARRAYCOUNT(Biomes));
|
||||
for (int i = 0; i < ARRAYCOUNT(Biomes); i++)
|
||||
{
|
||||
m_Biomes.push_back(Biomes[i]);
|
||||
}
|
||||
m_BiomesCount = (int)m_Biomes.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenCheckerboard:
|
||||
|
||||
void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
int Base = cChunkDef::Width * a_ChunkZ + z;
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Add = cChunkDef::Width * a_ChunkX + x;
|
||||
a_BiomeMap[x + cChunkDef::Width * z] = m_Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % m_BiomesCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenVoronoi :
|
||||
|
||||
void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
int BaseZ = cChunkDef::Width * a_ChunkZ;
|
||||
int BaseX = cChunkDef::Width * a_ChunkX;
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
int AbsoluteZ = BaseZ + z;
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ));
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ)
|
||||
{
|
||||
int CellX = a_BlockX / m_CellSize;
|
||||
int CellZ = a_BlockZ / m_CellSize;
|
||||
|
||||
// Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution
|
||||
|
||||
// Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell
|
||||
double MinDist = m_CellSize * m_CellSize; // There has to be a cell closer than this
|
||||
EMCSBiome res = biPlains; // Will be overriden
|
||||
for (int x = CellX - 2; x <= CellX + 2; x++)
|
||||
{
|
||||
int BaseX = x * m_CellSize;
|
||||
for (int z = CellZ - 2; z < CellZ + 2; z++)
|
||||
{
|
||||
int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize;
|
||||
int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize;
|
||||
int SeedX = BaseX + OffsetX;
|
||||
int SeedZ = z * m_CellSize + OffsetZ;
|
||||
|
||||
double Dist = sqrt((double)((SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ)));
|
||||
if (Dist < MinDist)
|
||||
{
|
||||
MinDist = Dist;
|
||||
res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount];
|
||||
}
|
||||
} // for z
|
||||
} // for x
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenDistortedVoronoi:
|
||||
|
||||
void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
|
||||
{
|
||||
int BaseZ = cChunkDef::Width * a_ChunkZ;
|
||||
int BaseX = cChunkDef::Width * a_ChunkX;
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
int AbsoluteZ = BaseZ + z;
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int DistX, DistZ;
|
||||
Distort(BaseX + x, AbsoluteZ, DistX, DistZ);
|
||||
cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistX, DistZ));
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBioGenDistortedVoronoi::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ)
|
||||
{
|
||||
double NoiseX = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 1000);
|
||||
NoiseX += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 2000);
|
||||
NoiseX += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 3000);
|
||||
double NoiseZ = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 4000);
|
||||
NoiseZ += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 5000);
|
||||
NoiseZ += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 6000);
|
||||
|
||||
a_DistortedX = a_BlockX + (int)(m_CellSize * 0.5 * NoiseX);
|
||||
a_DistortedZ = a_BlockZ + (int)(m_CellSize * 0.5 * NoiseZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
342
source/BioGen.h
342
source/BioGen.h
@@ -1,171 +1,171 @@
|
||||
|
||||
// BioGen.h
|
||||
|
||||
/*
|
||||
Interfaces to the various biome generators:
|
||||
- cBioGenConstant
|
||||
- cBioGenCheckerboard
|
||||
- cBioGenDistortedVoronoi
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cChunkGenerator.h"
|
||||
#include "cNoise.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenConstant :
|
||||
public cBiomeGen
|
||||
{
|
||||
public:
|
||||
cBioGenConstant(EMCSBiome a_Biome) : m_Biome(a_Biome) {}
|
||||
|
||||
protected:
|
||||
|
||||
EMCSBiome m_Biome;
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// A simple cache that stores N most recently generated chunks' biomes; N being settable upon creation
|
||||
class cBioGenCache :
|
||||
public cBiomeGen
|
||||
{
|
||||
public:
|
||||
cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize); // Takes ownership of a_BioGenToCache
|
||||
~cBioGenCache();
|
||||
|
||||
protected:
|
||||
|
||||
cBiomeGen * m_BioGenToCache;
|
||||
|
||||
struct sCacheData
|
||||
{
|
||||
int m_ChunkX;
|
||||
int m_ChunkZ;
|
||||
cChunkDef::BiomeMap m_BiomeMap;
|
||||
} ;
|
||||
|
||||
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
|
||||
int m_CacheSize;
|
||||
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
|
||||
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
|
||||
|
||||
// Cache statistics
|
||||
int m_NumHits;
|
||||
int m_NumMisses;
|
||||
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
|
||||
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Base class for generators that use a list of available biomes. This class takes care of the list.
|
||||
class cBiomeGenList :
|
||||
public cBiomeGen
|
||||
{
|
||||
protected:
|
||||
cBiomeGenList(const AString & a_Biomes)
|
||||
{
|
||||
InitializeBiomes(a_Biomes);
|
||||
}
|
||||
|
||||
// List of biomes that the generator is allowed to generate:
|
||||
typedef std::vector<EMCSBiome> EMCSBiomes;
|
||||
EMCSBiomes m_Biomes;
|
||||
int m_BiomesCount; // Pulled out of m_Biomes for faster access
|
||||
|
||||
void InitializeBiomes(const AString & a_Biomes);
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenCheckerboard :
|
||||
public cBiomeGenList
|
||||
{
|
||||
public:
|
||||
cBioGenCheckerboard(int a_BiomeSize, const AString & a_Biomes) :
|
||||
cBiomeGenList(a_Biomes),
|
||||
m_BiomeSize((a_BiomeSize < 8) ? 8 : a_BiomeSize)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int m_BiomeSize;
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenVoronoi :
|
||||
public cBiomeGenList
|
||||
{
|
||||
public:
|
||||
cBioGenVoronoi(int a_Seed, int a_CellSize, const AString & a_Biomes) :
|
||||
cBiomeGenList(a_Biomes),
|
||||
m_CellSize((a_CellSize > 4) ? a_CellSize : 4),
|
||||
m_Noise(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int m_CellSize;
|
||||
|
||||
cNoise m_Noise;
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
|
||||
EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenDistortedVoronoi :
|
||||
public cBioGenVoronoi
|
||||
{
|
||||
public:
|
||||
cBioGenDistortedVoronoi(int a_Seed, int a_CellSize, const AString & a_Biomes) :
|
||||
cBioGenVoronoi(a_Seed, a_CellSize, a_Biomes)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
|
||||
/// Distorts the coords using a Perlin-like noise
|
||||
void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BioGen.h
|
||||
|
||||
/*
|
||||
Interfaces to the various biome generators:
|
||||
- cBioGenConstant
|
||||
- cBioGenCheckerboard
|
||||
- cBioGenDistortedVoronoi
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cChunkGenerator.h"
|
||||
#include "cNoise.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenConstant :
|
||||
public cBiomeGen
|
||||
{
|
||||
public:
|
||||
cBioGenConstant(EMCSBiome a_Biome) : m_Biome(a_Biome) {}
|
||||
|
||||
protected:
|
||||
|
||||
EMCSBiome m_Biome;
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// A simple cache that stores N most recently generated chunks' biomes; N being settable upon creation
|
||||
class cBioGenCache :
|
||||
public cBiomeGen
|
||||
{
|
||||
public:
|
||||
cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize); // Takes ownership of a_BioGenToCache
|
||||
~cBioGenCache();
|
||||
|
||||
protected:
|
||||
|
||||
cBiomeGen * m_BioGenToCache;
|
||||
|
||||
struct sCacheData
|
||||
{
|
||||
int m_ChunkX;
|
||||
int m_ChunkZ;
|
||||
cChunkDef::BiomeMap m_BiomeMap;
|
||||
} ;
|
||||
|
||||
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
|
||||
int m_CacheSize;
|
||||
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
|
||||
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
|
||||
|
||||
// Cache statistics
|
||||
int m_NumHits;
|
||||
int m_NumMisses;
|
||||
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
|
||||
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Base class for generators that use a list of available biomes. This class takes care of the list.
|
||||
class cBiomeGenList :
|
||||
public cBiomeGen
|
||||
{
|
||||
protected:
|
||||
cBiomeGenList(const AString & a_Biomes)
|
||||
{
|
||||
InitializeBiomes(a_Biomes);
|
||||
}
|
||||
|
||||
// List of biomes that the generator is allowed to generate:
|
||||
typedef std::vector<EMCSBiome> EMCSBiomes;
|
||||
EMCSBiomes m_Biomes;
|
||||
int m_BiomesCount; // Pulled out of m_Biomes for faster access
|
||||
|
||||
void InitializeBiomes(const AString & a_Biomes);
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenCheckerboard :
|
||||
public cBiomeGenList
|
||||
{
|
||||
public:
|
||||
cBioGenCheckerboard(int a_BiomeSize, const AString & a_Biomes) :
|
||||
cBiomeGenList(a_Biomes),
|
||||
m_BiomeSize((a_BiomeSize < 8) ? 8 : a_BiomeSize)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int m_BiomeSize;
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenVoronoi :
|
||||
public cBiomeGenList
|
||||
{
|
||||
public:
|
||||
cBioGenVoronoi(int a_Seed, int a_CellSize, const AString & a_Biomes) :
|
||||
cBiomeGenList(a_Biomes),
|
||||
m_CellSize((a_CellSize > 4) ? a_CellSize : 4),
|
||||
m_Noise(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int m_CellSize;
|
||||
|
||||
cNoise m_Noise;
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
|
||||
EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBioGenDistortedVoronoi :
|
||||
public cBioGenVoronoi
|
||||
{
|
||||
public:
|
||||
cBioGenDistortedVoronoi(int a_Seed, int a_CellSize, const AString & a_Biomes) :
|
||||
cBioGenVoronoi(a_Seed, a_CellSize, a_Biomes)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// cBiomeGen override:
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
|
||||
|
||||
/// Distorts the coords using a Perlin-like noise
|
||||
void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,325 +1,325 @@
|
||||
|
||||
// BlockID.cpp
|
||||
|
||||
// Implements the helper functions for converting Block ID string to int etc.
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BlockID.h"
|
||||
#include "../iniFile/iniFile.h"
|
||||
#include "cItem.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE g_BlockLightValue[256];
|
||||
NIBBLETYPE g_BlockSpreadLightFalloff[256];
|
||||
bool g_BlockTransparent[256];
|
||||
bool g_BlockOneHitDig[256];
|
||||
bool g_BlockPistonBreakable[256];
|
||||
bool g_BlockIsSnowable[256];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBlockIDMap
|
||||
{
|
||||
public:
|
||||
cBlockIDMap(void) : m_Ini("items.ini")
|
||||
{
|
||||
m_Ini.ReadFile();
|
||||
}
|
||||
|
||||
int Resolve(const AString & a_ItemName)
|
||||
{
|
||||
return m_Ini.GetValueI("Items", a_ItemName, -1);
|
||||
}
|
||||
|
||||
AString ResolveString(const AString & a_ItemName)
|
||||
{
|
||||
return m_Ini.GetValue("Items", a_ItemName, "");
|
||||
}
|
||||
|
||||
protected:
|
||||
cIniFile m_Ini;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static cBlockIDMap gsBlockIDMap;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int BlockStringToType(const AString & a_BlockTypeString)
|
||||
{
|
||||
int res = atoi(a_BlockTypeString.c_str());
|
||||
if ((res != 0) || (a_BlockTypeString.compare("0") == 0))
|
||||
{
|
||||
// It was a valid number, return that
|
||||
return res;
|
||||
}
|
||||
|
||||
return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
|
||||
{
|
||||
AString Resolved = TrimString(gsBlockIDMap.ResolveString(TrimString(a_ItemTypeString)));
|
||||
AString txt = (!Resolved.empty()) ? Resolved : a_ItemTypeString;
|
||||
AStringVector Split = StringSplit(txt, ":");
|
||||
if (Split.size() == 1)
|
||||
{
|
||||
Split = StringSplit(txt, "^");
|
||||
}
|
||||
if (Split.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
a_Item.m_ItemID = (ENUM_ITEM_ID)atoi(Split[0].c_str());
|
||||
if ((a_Item.m_ItemID == 0) && (Split[0] != "0"))
|
||||
{
|
||||
// Parsing the number failed
|
||||
return false;
|
||||
}
|
||||
if (Split.size() > 1)
|
||||
{
|
||||
a_Item.m_ItemHealth = atoi(Split[1].c_str());
|
||||
if ((a_Item.m_ItemHealth == 0) && (Split[1] != "0"))
|
||||
{
|
||||
// Parsing the number failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EMCSBiome StringToBiome(const AString & a_BiomeString)
|
||||
{
|
||||
// If it is a number, return it:
|
||||
int res = atoi(a_BiomeString.c_str());
|
||||
if ((res != 0) || (a_BiomeString.compare("0") == 0))
|
||||
{
|
||||
// It was a valid number
|
||||
return (EMCSBiome)res;
|
||||
}
|
||||
|
||||
// Convert using the built-in map:
|
||||
static struct {
|
||||
EMCSBiome m_Biome;
|
||||
const char * m_String;
|
||||
} BiomeMap[] =
|
||||
{
|
||||
{biOcean, "Ocean"} ,
|
||||
{biPlains, "Plains"},
|
||||
{biDesert, "Desert"},
|
||||
{biExtremeHills, "ExtremeHills"},
|
||||
{biForest, "Forest"},
|
||||
{biTaiga, "Taiga"},
|
||||
{biSwampland, "Swampland"},
|
||||
{biRiver, "River"},
|
||||
{biHell, "Hell"},
|
||||
{biHell, "Nether"},
|
||||
{biSky, "Sky"},
|
||||
{biFrozenOcean, "FrozenOcean"},
|
||||
{biFrozenRiver, "FrozenRiver"},
|
||||
{biIcePlains, "IcePlains"},
|
||||
{biIcePlains, "Tundra"},
|
||||
{biIceMountains, "IceMountains"},
|
||||
{biMushroomIsland, "MushroomIsland"},
|
||||
{biMushroomShore, "MushroomShore"},
|
||||
{biBeach, "Beach"},
|
||||
{biDesertHills, "DesertHills"},
|
||||
{biForestHills, "ForestHills"},
|
||||
{biTaigaHills, "TaigaHills"},
|
||||
{biExtremeHillsEdge, "ExtremeHillsEdge"},
|
||||
{biJungle, "Jungle"},
|
||||
{biJungleHills, "JungleHills"},
|
||||
} ;
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(BiomeMap); i++)
|
||||
{
|
||||
if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0)
|
||||
{
|
||||
return BiomeMap[i].m_Biome;
|
||||
}
|
||||
} // for i - BiomeMap[]
|
||||
return (EMCSBiome)-1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor:
|
||||
class cBlockPropertiesInitializer
|
||||
{
|
||||
public:
|
||||
cBlockPropertiesInitializer(void)
|
||||
{
|
||||
memset( g_BlockLightValue, 0x00, sizeof( g_BlockLightValue ) );
|
||||
memset( g_BlockSpreadLightFalloff, 0x0f, sizeof( g_BlockSpreadLightFalloff ) ); // 0x0f means total falloff
|
||||
memset( g_BlockTransparent, 0x00, sizeof( g_BlockTransparent ) );
|
||||
memset( g_BlockOneHitDig, 0x00, sizeof( g_BlockOneHitDig ) );
|
||||
memset( g_BlockPistonBreakable, 0x00, sizeof( g_BlockPistonBreakable ) );
|
||||
memset( g_BlockIsSnowable, 0xff, sizeof( g_BlockIsSnowable)); // Set all blocks' snowable to true
|
||||
|
||||
// Emissive blocks
|
||||
g_BlockLightValue[E_BLOCK_FIRE] = 15;
|
||||
g_BlockLightValue[E_BLOCK_GLOWSTONE] = 15;
|
||||
g_BlockLightValue[E_BLOCK_JACK_O_LANTERN] = 15;
|
||||
g_BlockLightValue[E_BLOCK_LAVA] = 15;
|
||||
g_BlockLightValue[E_BLOCK_STATIONARY_LAVA] = 15;
|
||||
g_BlockLightValue[E_BLOCK_END_PORTAL] = 15;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_LAMP_ON] = 15;
|
||||
g_BlockLightValue[E_BLOCK_TORCH] = 14;
|
||||
g_BlockLightValue[E_BLOCK_BURNING_FURNACE] = 13;
|
||||
g_BlockLightValue[E_BLOCK_NETHER_PORTAL] = 11;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_ORE_GLOWING] = 9;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_REPEATER_ON] = 9;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_TORCH_ON] = 7;
|
||||
g_BlockLightValue[E_BLOCK_BREWING_STAND] = 1;
|
||||
g_BlockLightValue[E_BLOCK_BROWN_MUSHROOM] = 1;
|
||||
g_BlockLightValue[E_BLOCK_DRAGON_EGG] = 1;
|
||||
|
||||
// Spread blocks
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_AIR] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_CHEST] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_CROPS] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_FIRE] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_GLASS] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_GLOWSTONE] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_LEAVES] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_SIGN_POST] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_TORCH] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_VINES] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_WALLSIGN] = 1;
|
||||
// Light in water and lava dissapears faster:
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_LAVA] = 2;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_LAVA] = 2;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_WATER] = 2;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_WATER] = 2;
|
||||
|
||||
// Transparent blocks
|
||||
g_BlockTransparent[E_BLOCK_AIR] = true;
|
||||
g_BlockTransparent[E_BLOCK_BROWN_MUSHROOM] = true;
|
||||
g_BlockTransparent[E_BLOCK_CHEST] = true;
|
||||
g_BlockTransparent[E_BLOCK_CROPS] = true;
|
||||
g_BlockTransparent[E_BLOCK_FIRE] = true;
|
||||
g_BlockTransparent[E_BLOCK_GLASS] = true;
|
||||
g_BlockTransparent[E_BLOCK_ICE] = true;
|
||||
g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true;
|
||||
g_BlockTransparent[E_BLOCK_RED_ROSE] = true;
|
||||
g_BlockTransparent[E_BLOCK_SIGN_POST] = true;
|
||||
g_BlockTransparent[E_BLOCK_SNOW] = true;
|
||||
g_BlockTransparent[E_BLOCK_TALL_GRASS] = true;
|
||||
g_BlockTransparent[E_BLOCK_TORCH] = true;
|
||||
g_BlockTransparent[E_BLOCK_VINES] = true;
|
||||
g_BlockTransparent[E_BLOCK_WALLSIGN] = true;
|
||||
g_BlockTransparent[E_BLOCK_YELLOW_FLOWER] = true;
|
||||
|
||||
// TODO: Any other transparent blocks?
|
||||
|
||||
// One hit break blocks
|
||||
g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_CROPS] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_FIRE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_LOCKED_CHEST] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_OFF] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_ON] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_RED_ROSE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REEDS] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_SAPLING] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_TNT] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_TALL_GRASS] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_TORCH] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_YELLOW_FLOWER] = true;
|
||||
|
||||
// Blocks that breaks when pushed by piston
|
||||
g_BlockPistonBreakable[E_BLOCK_AIR] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_BED] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_COBWEB] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_CROPS] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_FIRE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_LADDER] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_LAVA] = false;
|
||||
g_BlockPistonBreakable[E_BLOCK_LEVER] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_MELON] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_MELON_STEM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_PUMPKIN] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_PUMPKIN_STEM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_OFF] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_ON] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REDSTONE_WIRE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_RED_MUSHROOM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_RED_ROSE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REEDS] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_SNOW] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_STATIONARY_LAVA] = false;
|
||||
g_BlockPistonBreakable[E_BLOCK_STATIONARY_WATER] = false; //This gave pistons the ability to drop water :D
|
||||
g_BlockPistonBreakable[E_BLOCK_STONE_BUTTON] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_STONE_PRESSURE_PLATE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_TALL_GRASS] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_TORCH] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_VINES] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_WATER] = false;
|
||||
g_BlockPistonBreakable[E_BLOCK_WOODEN_DOOR] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_WOODEN_PRESSURE_PLATE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_YELLOW_FLOWER] = true;
|
||||
|
||||
// Blocks that can be snowed over:
|
||||
g_BlockIsSnowable[E_BLOCK_BROWN_MUSHROOM] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_CACTUS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_CHEST] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_CROPS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_FIRE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_FIRE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_GLASS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_ICE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_LAVA] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_LOCKED_CHEST] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_OFF] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_ON] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_OFF] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_ON] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_WIRE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_RED_MUSHROOM] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_RED_ROSE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REEDS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_SAPLING] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_SIGN_POST] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_SNOW] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_STATIONARY_LAVA] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_STATIONARY_WATER] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_TALL_GRASS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_TNT] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_TORCH] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_WALLSIGN] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_WATER] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_YELLOW_FLOWER] = false;
|
||||
}
|
||||
} BlockPropertiesInitializer;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BlockID.cpp
|
||||
|
||||
// Implements the helper functions for converting Block ID string to int etc.
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BlockID.h"
|
||||
#include "../iniFile/iniFile.h"
|
||||
#include "cItem.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE g_BlockLightValue[256];
|
||||
NIBBLETYPE g_BlockSpreadLightFalloff[256];
|
||||
bool g_BlockTransparent[256];
|
||||
bool g_BlockOneHitDig[256];
|
||||
bool g_BlockPistonBreakable[256];
|
||||
bool g_BlockIsSnowable[256];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBlockIDMap
|
||||
{
|
||||
public:
|
||||
cBlockIDMap(void) : m_Ini("items.ini")
|
||||
{
|
||||
m_Ini.ReadFile();
|
||||
}
|
||||
|
||||
int Resolve(const AString & a_ItemName)
|
||||
{
|
||||
return m_Ini.GetValueI("Items", a_ItemName, -1);
|
||||
}
|
||||
|
||||
AString ResolveString(const AString & a_ItemName)
|
||||
{
|
||||
return m_Ini.GetValue("Items", a_ItemName, "");
|
||||
}
|
||||
|
||||
protected:
|
||||
cIniFile m_Ini;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static cBlockIDMap gsBlockIDMap;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int BlockStringToType(const AString & a_BlockTypeString)
|
||||
{
|
||||
int res = atoi(a_BlockTypeString.c_str());
|
||||
if ((res != 0) || (a_BlockTypeString.compare("0") == 0))
|
||||
{
|
||||
// It was a valid number, return that
|
||||
return res;
|
||||
}
|
||||
|
||||
return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
|
||||
{
|
||||
AString Resolved = TrimString(gsBlockIDMap.ResolveString(TrimString(a_ItemTypeString)));
|
||||
AString txt = (!Resolved.empty()) ? Resolved : a_ItemTypeString;
|
||||
AStringVector Split = StringSplit(txt, ":");
|
||||
if (Split.size() == 1)
|
||||
{
|
||||
Split = StringSplit(txt, "^");
|
||||
}
|
||||
if (Split.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
a_Item.m_ItemID = (ENUM_ITEM_ID)atoi(Split[0].c_str());
|
||||
if ((a_Item.m_ItemID == 0) && (Split[0] != "0"))
|
||||
{
|
||||
// Parsing the number failed
|
||||
return false;
|
||||
}
|
||||
if (Split.size() > 1)
|
||||
{
|
||||
a_Item.m_ItemHealth = atoi(Split[1].c_str());
|
||||
if ((a_Item.m_ItemHealth == 0) && (Split[1] != "0"))
|
||||
{
|
||||
// Parsing the number failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EMCSBiome StringToBiome(const AString & a_BiomeString)
|
||||
{
|
||||
// If it is a number, return it:
|
||||
int res = atoi(a_BiomeString.c_str());
|
||||
if ((res != 0) || (a_BiomeString.compare("0") == 0))
|
||||
{
|
||||
// It was a valid number
|
||||
return (EMCSBiome)res;
|
||||
}
|
||||
|
||||
// Convert using the built-in map:
|
||||
static struct {
|
||||
EMCSBiome m_Biome;
|
||||
const char * m_String;
|
||||
} BiomeMap[] =
|
||||
{
|
||||
{biOcean, "Ocean"} ,
|
||||
{biPlains, "Plains"},
|
||||
{biDesert, "Desert"},
|
||||
{biExtremeHills, "ExtremeHills"},
|
||||
{biForest, "Forest"},
|
||||
{biTaiga, "Taiga"},
|
||||
{biSwampland, "Swampland"},
|
||||
{biRiver, "River"},
|
||||
{biHell, "Hell"},
|
||||
{biHell, "Nether"},
|
||||
{biSky, "Sky"},
|
||||
{biFrozenOcean, "FrozenOcean"},
|
||||
{biFrozenRiver, "FrozenRiver"},
|
||||
{biIcePlains, "IcePlains"},
|
||||
{biIcePlains, "Tundra"},
|
||||
{biIceMountains, "IceMountains"},
|
||||
{biMushroomIsland, "MushroomIsland"},
|
||||
{biMushroomShore, "MushroomShore"},
|
||||
{biBeach, "Beach"},
|
||||
{biDesertHills, "DesertHills"},
|
||||
{biForestHills, "ForestHills"},
|
||||
{biTaigaHills, "TaigaHills"},
|
||||
{biExtremeHillsEdge, "ExtremeHillsEdge"},
|
||||
{biJungle, "Jungle"},
|
||||
{biJungleHills, "JungleHills"},
|
||||
} ;
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(BiomeMap); i++)
|
||||
{
|
||||
if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0)
|
||||
{
|
||||
return BiomeMap[i].m_Biome;
|
||||
}
|
||||
} // for i - BiomeMap[]
|
||||
return (EMCSBiome)-1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor:
|
||||
class cBlockPropertiesInitializer
|
||||
{
|
||||
public:
|
||||
cBlockPropertiesInitializer(void)
|
||||
{
|
||||
memset( g_BlockLightValue, 0x00, sizeof( g_BlockLightValue ) );
|
||||
memset( g_BlockSpreadLightFalloff, 0x0f, sizeof( g_BlockSpreadLightFalloff ) ); // 0x0f means total falloff
|
||||
memset( g_BlockTransparent, 0x00, sizeof( g_BlockTransparent ) );
|
||||
memset( g_BlockOneHitDig, 0x00, sizeof( g_BlockOneHitDig ) );
|
||||
memset( g_BlockPistonBreakable, 0x00, sizeof( g_BlockPistonBreakable ) );
|
||||
memset( g_BlockIsSnowable, 0xff, sizeof( g_BlockIsSnowable)); // Set all blocks' snowable to true
|
||||
|
||||
// Emissive blocks
|
||||
g_BlockLightValue[E_BLOCK_FIRE] = 15;
|
||||
g_BlockLightValue[E_BLOCK_GLOWSTONE] = 15;
|
||||
g_BlockLightValue[E_BLOCK_JACK_O_LANTERN] = 15;
|
||||
g_BlockLightValue[E_BLOCK_LAVA] = 15;
|
||||
g_BlockLightValue[E_BLOCK_STATIONARY_LAVA] = 15;
|
||||
g_BlockLightValue[E_BLOCK_END_PORTAL] = 15;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_LAMP_ON] = 15;
|
||||
g_BlockLightValue[E_BLOCK_TORCH] = 14;
|
||||
g_BlockLightValue[E_BLOCK_BURNING_FURNACE] = 13;
|
||||
g_BlockLightValue[E_BLOCK_NETHER_PORTAL] = 11;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_ORE_GLOWING] = 9;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_REPEATER_ON] = 9;
|
||||
g_BlockLightValue[E_BLOCK_REDSTONE_TORCH_ON] = 7;
|
||||
g_BlockLightValue[E_BLOCK_BREWING_STAND] = 1;
|
||||
g_BlockLightValue[E_BLOCK_BROWN_MUSHROOM] = 1;
|
||||
g_BlockLightValue[E_BLOCK_DRAGON_EGG] = 1;
|
||||
|
||||
// Spread blocks
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_AIR] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_CHEST] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_CROPS] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_FIRE] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_GLASS] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_GLOWSTONE] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_LEAVES] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_SIGN_POST] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_TORCH] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_VINES] = 1;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_WALLSIGN] = 1;
|
||||
// Light in water and lava dissapears faster:
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_LAVA] = 2;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_LAVA] = 2;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_WATER] = 2;
|
||||
g_BlockSpreadLightFalloff[E_BLOCK_WATER] = 2;
|
||||
|
||||
// Transparent blocks
|
||||
g_BlockTransparent[E_BLOCK_AIR] = true;
|
||||
g_BlockTransparent[E_BLOCK_BROWN_MUSHROOM] = true;
|
||||
g_BlockTransparent[E_BLOCK_CHEST] = true;
|
||||
g_BlockTransparent[E_BLOCK_CROPS] = true;
|
||||
g_BlockTransparent[E_BLOCK_FIRE] = true;
|
||||
g_BlockTransparent[E_BLOCK_GLASS] = true;
|
||||
g_BlockTransparent[E_BLOCK_ICE] = true;
|
||||
g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true;
|
||||
g_BlockTransparent[E_BLOCK_RED_ROSE] = true;
|
||||
g_BlockTransparent[E_BLOCK_SIGN_POST] = true;
|
||||
g_BlockTransparent[E_BLOCK_SNOW] = true;
|
||||
g_BlockTransparent[E_BLOCK_TALL_GRASS] = true;
|
||||
g_BlockTransparent[E_BLOCK_TORCH] = true;
|
||||
g_BlockTransparent[E_BLOCK_VINES] = true;
|
||||
g_BlockTransparent[E_BLOCK_WALLSIGN] = true;
|
||||
g_BlockTransparent[E_BLOCK_YELLOW_FLOWER] = true;
|
||||
|
||||
// TODO: Any other transparent blocks?
|
||||
|
||||
// One hit break blocks
|
||||
g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_CROPS] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_FIRE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_LOCKED_CHEST] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_OFF] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_ON] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_RED_ROSE] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_REEDS] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_SAPLING] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_TNT] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_TALL_GRASS] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_TORCH] = true;
|
||||
g_BlockOneHitDig[E_BLOCK_YELLOW_FLOWER] = true;
|
||||
|
||||
// Blocks that breaks when pushed by piston
|
||||
g_BlockPistonBreakable[E_BLOCK_AIR] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_BED] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_COBWEB] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_CROPS] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_FIRE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_LADDER] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_LAVA] = false;
|
||||
g_BlockPistonBreakable[E_BLOCK_LEVER] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_MELON] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_MELON_STEM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_PUMPKIN] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_PUMPKIN_STEM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_OFF] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_ON] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REDSTONE_WIRE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_RED_MUSHROOM] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_RED_ROSE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_REEDS] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_SNOW] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_STATIONARY_LAVA] = false;
|
||||
g_BlockPistonBreakable[E_BLOCK_STATIONARY_WATER] = false; //This gave pistons the ability to drop water :D
|
||||
g_BlockPistonBreakable[E_BLOCK_STONE_BUTTON] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_STONE_PRESSURE_PLATE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_TALL_GRASS] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_TORCH] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_VINES] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_WATER] = false;
|
||||
g_BlockPistonBreakable[E_BLOCK_WOODEN_DOOR] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_WOODEN_PRESSURE_PLATE] = true;
|
||||
g_BlockPistonBreakable[E_BLOCK_YELLOW_FLOWER] = true;
|
||||
|
||||
// Blocks that can be snowed over:
|
||||
g_BlockIsSnowable[E_BLOCK_BROWN_MUSHROOM] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_CACTUS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_CHEST] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_CROPS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_FIRE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_FIRE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_GLASS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_ICE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_LAVA] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_LOCKED_CHEST] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_OFF] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_ON] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_OFF] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_ON] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REDSTONE_WIRE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_RED_MUSHROOM] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_RED_ROSE] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_REEDS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_SAPLING] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_SIGN_POST] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_SNOW] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_STATIONARY_LAVA] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_STATIONARY_WATER] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_TALL_GRASS] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_TNT] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_TORCH] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_WALLSIGN] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_WATER] = false;
|
||||
g_BlockIsSnowable[E_BLOCK_YELLOW_FLOWER] = false;
|
||||
}
|
||||
} BlockPropertiesInitializer;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1282
source/BlockID.h
1282
source/BlockID.h
File diff suppressed because it is too large
Load Diff
@@ -1,492 +1,492 @@
|
||||
|
||||
// ChunkDef.h
|
||||
|
||||
// Interfaces to helper types for chunk definitions. Most modules want to include this instead of cChunk.h
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Vector3i.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord.
|
||||
It will help us when the new chunk format comes out and we need to patch everything up for compatibility.
|
||||
*/
|
||||
#define ZERO_CHUNK_Y 0
|
||||
|
||||
// Used to smoothly convert to new axis ordering. One will be removed when deemed stable.
|
||||
#define AXIS_ORDER_YZX 1 // Original (1.1-)
|
||||
#define AXIS_ORDER_XZY 2 // New (1.2+)
|
||||
#define AXIS_ORDER AXIS_ORDER_XZY
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd
|
||||
class cBlockEntity;
|
||||
class cEntity;
|
||||
class cClientHandle;
|
||||
class cBlockEntity;
|
||||
|
||||
typedef std::list<cEntity *> cEntityList;
|
||||
typedef std::list<cBlockEntity *> cBlockEntityList;
|
||||
|
||||
|
||||
|
||||
|
||||
/// The datatype used by blockdata
|
||||
typedef char BLOCKTYPE;
|
||||
|
||||
/// The datatype used by nibbledata (meta, light, skylight)
|
||||
typedef unsigned char NIBBLETYPE;
|
||||
|
||||
/// The type used by the heightmap
|
||||
typedef unsigned char HEIGHTTYPE;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Biome IDs
|
||||
The first batch corresponds to the clientside biomes, used by MineCraft.
|
||||
BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client
|
||||
*/
|
||||
enum EMCSBiome
|
||||
{
|
||||
biOcean = 0,
|
||||
biPlains = 1,
|
||||
biDesert = 2,
|
||||
biExtremeHills = 3,
|
||||
biForest = 4,
|
||||
biTaiga = 5,
|
||||
biSwampland = 6,
|
||||
biRiver = 7,
|
||||
biHell = 8, // same as Nether
|
||||
biNether = 8,
|
||||
biSky = 9,
|
||||
biFrozenOcean = 10,
|
||||
biFrozenRiver = 11,
|
||||
biIcePlains = 12,
|
||||
biTundra = 12, // same as Ice Plains
|
||||
biIceMountains = 13,
|
||||
biMushroomIsland = 14,
|
||||
biMushroomShore = 15,
|
||||
biBeach = 16,
|
||||
biDesertHills = 17,
|
||||
biForestHills = 18,
|
||||
biTaigaHills = 19,
|
||||
biExtremeHillsEdge = 20,
|
||||
biJungle = 21,
|
||||
biJungleHills = 22,
|
||||
|
||||
// Automatically capture the maximum biome value into biMaxBiome:
|
||||
biNumBiomes, // True number of biomes, since they are zero-based
|
||||
biMaxBiome = biNumBiomes - 1, // The maximum biome value
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Constants used throughout the code, useful typedefs and utility functions
|
||||
class cChunkDef
|
||||
{
|
||||
public:
|
||||
static const int Width = 16;
|
||||
static const int Height = 256;
|
||||
static const int NumBlocks = Width * Height * Width;
|
||||
static const int BlockDataSize = NumBlocks * 2 + (NumBlocks / 2); // 2.5 * numblocks
|
||||
|
||||
// Offsets to individual components in the joined blockdata array
|
||||
static const int MetaOffset = NumBlocks;
|
||||
static const int LightOffset = MetaOffset + NumBlocks / 2;
|
||||
static const int SkyLightOffset = LightOffset + NumBlocks / 2;
|
||||
|
||||
static const unsigned int INDEX_OUT_OF_RANGE = 0xffffffff;
|
||||
|
||||
/// The type used for any heightmap operations and storage; idx = x + Width * z
|
||||
typedef HEIGHTTYPE HeightMap[Width * Width];
|
||||
|
||||
/** The type used for any biomemap operations and storage inside MCServer,
|
||||
using MCServer biomes (need not correspond to client representation!)
|
||||
idx = x + Width * z // Need to verify this with the protocol spec, currently unknown!
|
||||
*/
|
||||
typedef EMCSBiome BiomeMap[Width * Width];
|
||||
|
||||
/// The type used for block type operations and storage, AXIS_ORDER ordering
|
||||
typedef BLOCKTYPE BlockTypes[NumBlocks];
|
||||
|
||||
/// The type used for block data in nibble format, AXIS_ORDER ordering
|
||||
typedef NIBBLETYPE BlockNibbles[NumBlocks / 2];
|
||||
|
||||
|
||||
/// Converts absolute block coords into relative (chunk + block) coords:
|
||||
inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
|
||||
{
|
||||
BlockToChunk(a_X, a_Y, a_Z, a_ChunkX, a_ChunkZ);
|
||||
|
||||
a_X = a_X - a_ChunkX * Width;
|
||||
a_Z = a_Z - a_ChunkZ * Width;
|
||||
}
|
||||
|
||||
|
||||
/// Converts absolute block coords to chunk coords:
|
||||
inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkZ )
|
||||
{
|
||||
(void)a_Y;
|
||||
a_ChunkX = a_X / Width;
|
||||
if ((a_X < 0) && (a_X % Width != 0))
|
||||
{
|
||||
a_ChunkX--;
|
||||
}
|
||||
a_ChunkZ = a_Z / cChunkDef::Width;
|
||||
if ((a_Z < 0) && (a_Z % Width != 0))
|
||||
{
|
||||
a_ChunkZ--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline static unsigned int MakeIndex(int x, int y, int z )
|
||||
{
|
||||
if( x < cChunkDef::Width && x > -1 && y < cChunkDef::Height && y > -1 && z < cChunkDef::Width && z > -1 )
|
||||
{
|
||||
return MakeIndexNoCheck(x, y, z);
|
||||
}
|
||||
return INDEX_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
|
||||
inline static unsigned int MakeIndexNoCheck(int x, int y, int z)
|
||||
{
|
||||
#if AXIS_ORDER == AXIS_ORDER_XZY
|
||||
// For some reason, NOT using the Horner schema is faster. Weird.
|
||||
return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 is XZY
|
||||
#elif AXIS_ORDER == AXIS_ORDER_YZX
|
||||
return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 is YZX
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline static Vector3i IndexToCoordinate( unsigned int index )
|
||||
{
|
||||
#if AXIS_ORDER == AXIS_ORDER_XZY
|
||||
return Vector3i( // 1.2
|
||||
index % cChunkDef::Width, // X
|
||||
index / (cChunkDef::Width * cChunkDef::Width), // Y
|
||||
(index / cChunkDef::Width) % cChunkDef::Width // Z
|
||||
);
|
||||
#elif AXIS_ORDER == AXIS_ORDER_YZX
|
||||
return Vector3i( // 1.1
|
||||
index / (cChunkDef::Height * cChunkDef::Width), // X
|
||||
index % cChunkDef::Height, // Y
|
||||
(index / cChunkDef::Height) % cChunkDef::Width // Z
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Y >= 0) && (a_Y < Height));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type;
|
||||
}
|
||||
|
||||
|
||||
inline static BLOCKTYPE GetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Y >= 0) && (a_Y < Height));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)];
|
||||
}
|
||||
|
||||
|
||||
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
|
||||
{
|
||||
return a_HeightMap[a_X + Width * a_Z];
|
||||
}
|
||||
|
||||
|
||||
inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
|
||||
{
|
||||
a_HeightMap[a_X + Width * a_Z] = a_Height;
|
||||
}
|
||||
|
||||
|
||||
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
|
||||
{
|
||||
return a_BiomeMap[a_X + Width * a_Z];
|
||||
}
|
||||
|
||||
|
||||
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
|
||||
{
|
||||
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
|
||||
}
|
||||
|
||||
|
||||
static NIBBLETYPE GetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx)
|
||||
{
|
||||
if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
|
||||
{
|
||||
return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static NIBBLETYPE GetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z)
|
||||
{
|
||||
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
|
||||
{
|
||||
int Index = MakeIndexNoCheck(x, y, z);
|
||||
return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
|
||||
{
|
||||
if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
|
||||
{
|
||||
a_Buffer[a_BlockIdx / 2] = (
|
||||
(a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
|
||||
((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
|
||||
{
|
||||
if ((x < cChunkDef::Width) && (x > -1) && (y < cChunkDef::Height) && (y > -1) && (z < cChunkDef::Width) && (z > -1))
|
||||
{
|
||||
int Index = MakeIndexNoCheck(x, y, z);
|
||||
a_Buffer[Index / 2] = (
|
||||
(a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
|
||||
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline static char GetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
|
||||
{
|
||||
return GetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
|
||||
}
|
||||
|
||||
|
||||
inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value )
|
||||
{
|
||||
SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value );
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface class used for getting data out of a chunk using the GetAllData() function.
|
||||
Implementation must use the pointers immediately and NOT store any of them for later use
|
||||
The virtual methods are called in the same order as they're declared here.
|
||||
*/
|
||||
class cChunkDataCallback abstract
|
||||
{
|
||||
public:
|
||||
/// Called once to provide heightmap data
|
||||
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {};
|
||||
|
||||
/// Called once to provide biome data
|
||||
virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {};
|
||||
|
||||
/// Called once to export block types
|
||||
virtual void BlockTypes (const BLOCKTYPE * a_Type) {};
|
||||
|
||||
/// Called once to export block meta
|
||||
virtual void BlockMeta (const NIBBLETYPE * a_Meta) {};
|
||||
|
||||
/// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next
|
||||
virtual bool LightIsValid(bool a_IsLightValid) {return true; };
|
||||
|
||||
/// Called once to export block light
|
||||
virtual void BlockLight (const NIBBLETYPE * a_Meta) {};
|
||||
|
||||
/// Called once to export sky light
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_Meta) {};
|
||||
|
||||
/// Called for each entity in the chunk
|
||||
virtual void Entity(cEntity * a_Entity) {};
|
||||
|
||||
/// Called for each blockentity in the chunk
|
||||
virtual void BlockEntity(cBlockEntity * a_Entity) {};
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
|
||||
*/
|
||||
class cChunkDataCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
// Must be char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
|
||||
char m_BlockData[cChunkDef::BlockDataSize];
|
||||
|
||||
protected:
|
||||
|
||||
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
|
||||
{
|
||||
memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
|
||||
{
|
||||
memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
|
||||
{
|
||||
memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
|
||||
{
|
||||
memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers
|
||||
*/
|
||||
class cChunkDataSeparateCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkDef::BlockTypes m_BlockTypes;
|
||||
cChunkDef::BlockNibbles m_BlockMetas;
|
||||
cChunkDef::BlockNibbles m_BlockLight;
|
||||
cChunkDef::BlockNibbles m_BlockSkyLight;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
|
||||
{
|
||||
memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
|
||||
{
|
||||
memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
|
||||
{
|
||||
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
|
||||
{
|
||||
memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface class used for comparing clients of two chunks.
|
||||
Used primarily for entity moving while both chunks are locked.
|
||||
*/
|
||||
class cClientDiffCallback
|
||||
{
|
||||
public:
|
||||
/// Called for clients that are in Chunk1 and not in Chunk2,
|
||||
virtual void Removed(cClientHandle * a_Client) = 0;
|
||||
|
||||
/// Called for clients that are in Chunk2 and not in Chunk1.
|
||||
virtual void Added(cClientHandle * a_Client) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct sSetBlock
|
||||
{
|
||||
int x, y, z;
|
||||
int ChunkX, ChunkZ;
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
|
||||
sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); // absolute block position
|
||||
sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
|
||||
ChunkX(a_ChunkX), ChunkZ(a_ChunkZ),
|
||||
x(a_X), y(a_Y), z(a_Z),
|
||||
BlockType(a_BlockType),
|
||||
BlockMeta(a_BlockMeta)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::list<sSetBlock> sSetBlockList;
|
||||
typedef std::vector<sSetBlock> sSetBlockVector;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cChunkCoords
|
||||
{
|
||||
public:
|
||||
int m_ChunkX;
|
||||
int m_ChunkY;
|
||||
int m_ChunkZ;
|
||||
|
||||
cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {}
|
||||
|
||||
bool operator == (const cChunkCoords & a_Other) const
|
||||
{
|
||||
return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ));
|
||||
}
|
||||
} ;
|
||||
|
||||
typedef std::list<cChunkCoords> cChunkCoordsList;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Interface class used as a callback for operations that involve chunk coords
|
||||
class cChunkCoordCallback
|
||||
{
|
||||
public:
|
||||
virtual void Call(int a_ChunkX, int a_ChunkZ) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ChunkDef.h
|
||||
|
||||
// Interfaces to helper types for chunk definitions. Most modules want to include this instead of cChunk.h
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Vector3i.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord.
|
||||
It will help us when the new chunk format comes out and we need to patch everything up for compatibility.
|
||||
*/
|
||||
#define ZERO_CHUNK_Y 0
|
||||
|
||||
// Used to smoothly convert to new axis ordering. One will be removed when deemed stable.
|
||||
#define AXIS_ORDER_YZX 1 // Original (1.1-)
|
||||
#define AXIS_ORDER_XZY 2 // New (1.2+)
|
||||
#define AXIS_ORDER AXIS_ORDER_XZY
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd
|
||||
class cBlockEntity;
|
||||
class cEntity;
|
||||
class cClientHandle;
|
||||
class cBlockEntity;
|
||||
|
||||
typedef std::list<cEntity *> cEntityList;
|
||||
typedef std::list<cBlockEntity *> cBlockEntityList;
|
||||
|
||||
|
||||
|
||||
|
||||
/// The datatype used by blockdata
|
||||
typedef char BLOCKTYPE;
|
||||
|
||||
/// The datatype used by nibbledata (meta, light, skylight)
|
||||
typedef unsigned char NIBBLETYPE;
|
||||
|
||||
/// The type used by the heightmap
|
||||
typedef unsigned char HEIGHTTYPE;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Biome IDs
|
||||
The first batch corresponds to the clientside biomes, used by MineCraft.
|
||||
BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client
|
||||
*/
|
||||
enum EMCSBiome
|
||||
{
|
||||
biOcean = 0,
|
||||
biPlains = 1,
|
||||
biDesert = 2,
|
||||
biExtremeHills = 3,
|
||||
biForest = 4,
|
||||
biTaiga = 5,
|
||||
biSwampland = 6,
|
||||
biRiver = 7,
|
||||
biHell = 8, // same as Nether
|
||||
biNether = 8,
|
||||
biSky = 9,
|
||||
biFrozenOcean = 10,
|
||||
biFrozenRiver = 11,
|
||||
biIcePlains = 12,
|
||||
biTundra = 12, // same as Ice Plains
|
||||
biIceMountains = 13,
|
||||
biMushroomIsland = 14,
|
||||
biMushroomShore = 15,
|
||||
biBeach = 16,
|
||||
biDesertHills = 17,
|
||||
biForestHills = 18,
|
||||
biTaigaHills = 19,
|
||||
biExtremeHillsEdge = 20,
|
||||
biJungle = 21,
|
||||
biJungleHills = 22,
|
||||
|
||||
// Automatically capture the maximum biome value into biMaxBiome:
|
||||
biNumBiomes, // True number of biomes, since they are zero-based
|
||||
biMaxBiome = biNumBiomes - 1, // The maximum biome value
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Constants used throughout the code, useful typedefs and utility functions
|
||||
class cChunkDef
|
||||
{
|
||||
public:
|
||||
static const int Width = 16;
|
||||
static const int Height = 256;
|
||||
static const int NumBlocks = Width * Height * Width;
|
||||
static const int BlockDataSize = NumBlocks * 2 + (NumBlocks / 2); // 2.5 * numblocks
|
||||
|
||||
// Offsets to individual components in the joined blockdata array
|
||||
static const int MetaOffset = NumBlocks;
|
||||
static const int LightOffset = MetaOffset + NumBlocks / 2;
|
||||
static const int SkyLightOffset = LightOffset + NumBlocks / 2;
|
||||
|
||||
static const unsigned int INDEX_OUT_OF_RANGE = 0xffffffff;
|
||||
|
||||
/// The type used for any heightmap operations and storage; idx = x + Width * z
|
||||
typedef HEIGHTTYPE HeightMap[Width * Width];
|
||||
|
||||
/** The type used for any biomemap operations and storage inside MCServer,
|
||||
using MCServer biomes (need not correspond to client representation!)
|
||||
idx = x + Width * z // Need to verify this with the protocol spec, currently unknown!
|
||||
*/
|
||||
typedef EMCSBiome BiomeMap[Width * Width];
|
||||
|
||||
/// The type used for block type operations and storage, AXIS_ORDER ordering
|
||||
typedef BLOCKTYPE BlockTypes[NumBlocks];
|
||||
|
||||
/// The type used for block data in nibble format, AXIS_ORDER ordering
|
||||
typedef NIBBLETYPE BlockNibbles[NumBlocks / 2];
|
||||
|
||||
|
||||
/// Converts absolute block coords into relative (chunk + block) coords:
|
||||
inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
|
||||
{
|
||||
BlockToChunk(a_X, a_Y, a_Z, a_ChunkX, a_ChunkZ);
|
||||
|
||||
a_X = a_X - a_ChunkX * Width;
|
||||
a_Z = a_Z - a_ChunkZ * Width;
|
||||
}
|
||||
|
||||
|
||||
/// Converts absolute block coords to chunk coords:
|
||||
inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkZ )
|
||||
{
|
||||
(void)a_Y;
|
||||
a_ChunkX = a_X / Width;
|
||||
if ((a_X < 0) && (a_X % Width != 0))
|
||||
{
|
||||
a_ChunkX--;
|
||||
}
|
||||
a_ChunkZ = a_Z / cChunkDef::Width;
|
||||
if ((a_Z < 0) && (a_Z % Width != 0))
|
||||
{
|
||||
a_ChunkZ--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline static unsigned int MakeIndex(int x, int y, int z )
|
||||
{
|
||||
if( x < cChunkDef::Width && x > -1 && y < cChunkDef::Height && y > -1 && z < cChunkDef::Width && z > -1 )
|
||||
{
|
||||
return MakeIndexNoCheck(x, y, z);
|
||||
}
|
||||
return INDEX_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
|
||||
inline static unsigned int MakeIndexNoCheck(int x, int y, int z)
|
||||
{
|
||||
#if AXIS_ORDER == AXIS_ORDER_XZY
|
||||
// For some reason, NOT using the Horner schema is faster. Weird.
|
||||
return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 is XZY
|
||||
#elif AXIS_ORDER == AXIS_ORDER_YZX
|
||||
return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 is YZX
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline static Vector3i IndexToCoordinate( unsigned int index )
|
||||
{
|
||||
#if AXIS_ORDER == AXIS_ORDER_XZY
|
||||
return Vector3i( // 1.2
|
||||
index % cChunkDef::Width, // X
|
||||
index / (cChunkDef::Width * cChunkDef::Width), // Y
|
||||
(index / cChunkDef::Width) % cChunkDef::Width // Z
|
||||
);
|
||||
#elif AXIS_ORDER == AXIS_ORDER_YZX
|
||||
return Vector3i( // 1.1
|
||||
index / (cChunkDef::Height * cChunkDef::Width), // X
|
||||
index % cChunkDef::Height, // Y
|
||||
(index / cChunkDef::Height) % cChunkDef::Width // Z
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Y >= 0) && (a_Y < Height));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type;
|
||||
}
|
||||
|
||||
|
||||
inline static BLOCKTYPE GetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Y >= 0) && (a_Y < Height));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)];
|
||||
}
|
||||
|
||||
|
||||
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
|
||||
{
|
||||
return a_HeightMap[a_X + Width * a_Z];
|
||||
}
|
||||
|
||||
|
||||
inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
|
||||
{
|
||||
a_HeightMap[a_X + Width * a_Z] = a_Height;
|
||||
}
|
||||
|
||||
|
||||
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
|
||||
{
|
||||
return a_BiomeMap[a_X + Width * a_Z];
|
||||
}
|
||||
|
||||
|
||||
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
|
||||
{
|
||||
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
|
||||
}
|
||||
|
||||
|
||||
static NIBBLETYPE GetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx)
|
||||
{
|
||||
if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
|
||||
{
|
||||
return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static NIBBLETYPE GetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z)
|
||||
{
|
||||
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
|
||||
{
|
||||
int Index = MakeIndexNoCheck(x, y, z);
|
||||
return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
|
||||
{
|
||||
if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
|
||||
{
|
||||
a_Buffer[a_BlockIdx / 2] = (
|
||||
(a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
|
||||
((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
|
||||
{
|
||||
if ((x < cChunkDef::Width) && (x > -1) && (y < cChunkDef::Height) && (y > -1) && (z < cChunkDef::Width) && (z > -1))
|
||||
{
|
||||
int Index = MakeIndexNoCheck(x, y, z);
|
||||
a_Buffer[Index / 2] = (
|
||||
(a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
|
||||
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline static char GetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
|
||||
{
|
||||
return GetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
|
||||
}
|
||||
|
||||
|
||||
inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value )
|
||||
{
|
||||
SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value );
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface class used for getting data out of a chunk using the GetAllData() function.
|
||||
Implementation must use the pointers immediately and NOT store any of them for later use
|
||||
The virtual methods are called in the same order as they're declared here.
|
||||
*/
|
||||
class cChunkDataCallback abstract
|
||||
{
|
||||
public:
|
||||
/// Called once to provide heightmap data
|
||||
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {};
|
||||
|
||||
/// Called once to provide biome data
|
||||
virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {};
|
||||
|
||||
/// Called once to export block types
|
||||
virtual void BlockTypes (const BLOCKTYPE * a_Type) {};
|
||||
|
||||
/// Called once to export block meta
|
||||
virtual void BlockMeta (const NIBBLETYPE * a_Meta) {};
|
||||
|
||||
/// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next
|
||||
virtual bool LightIsValid(bool a_IsLightValid) {return true; };
|
||||
|
||||
/// Called once to export block light
|
||||
virtual void BlockLight (const NIBBLETYPE * a_Meta) {};
|
||||
|
||||
/// Called once to export sky light
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_Meta) {};
|
||||
|
||||
/// Called for each entity in the chunk
|
||||
virtual void Entity(cEntity * a_Entity) {};
|
||||
|
||||
/// Called for each blockentity in the chunk
|
||||
virtual void BlockEntity(cBlockEntity * a_Entity) {};
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
|
||||
*/
|
||||
class cChunkDataCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
// Must be char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
|
||||
char m_BlockData[cChunkDef::BlockDataSize];
|
||||
|
||||
protected:
|
||||
|
||||
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
|
||||
{
|
||||
memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
|
||||
{
|
||||
memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
|
||||
{
|
||||
memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
|
||||
{
|
||||
memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers
|
||||
*/
|
||||
class cChunkDataSeparateCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkDef::BlockTypes m_BlockTypes;
|
||||
cChunkDef::BlockNibbles m_BlockMetas;
|
||||
cChunkDef::BlockNibbles m_BlockLight;
|
||||
cChunkDef::BlockNibbles m_BlockSkyLight;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
|
||||
{
|
||||
memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
|
||||
{
|
||||
memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
|
||||
{
|
||||
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
|
||||
}
|
||||
|
||||
|
||||
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
|
||||
{
|
||||
memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface class used for comparing clients of two chunks.
|
||||
Used primarily for entity moving while both chunks are locked.
|
||||
*/
|
||||
class cClientDiffCallback
|
||||
{
|
||||
public:
|
||||
/// Called for clients that are in Chunk1 and not in Chunk2,
|
||||
virtual void Removed(cClientHandle * a_Client) = 0;
|
||||
|
||||
/// Called for clients that are in Chunk2 and not in Chunk1.
|
||||
virtual void Added(cClientHandle * a_Client) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct sSetBlock
|
||||
{
|
||||
int x, y, z;
|
||||
int ChunkX, ChunkZ;
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
|
||||
sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); // absolute block position
|
||||
sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
|
||||
ChunkX(a_ChunkX), ChunkZ(a_ChunkZ),
|
||||
x(a_X), y(a_Y), z(a_Z),
|
||||
BlockType(a_BlockType),
|
||||
BlockMeta(a_BlockMeta)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::list<sSetBlock> sSetBlockList;
|
||||
typedef std::vector<sSetBlock> sSetBlockVector;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cChunkCoords
|
||||
{
|
||||
public:
|
||||
int m_ChunkX;
|
||||
int m_ChunkY;
|
||||
int m_ChunkZ;
|
||||
|
||||
cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {}
|
||||
|
||||
bool operator == (const cChunkCoords & a_Other) const
|
||||
{
|
||||
return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ));
|
||||
}
|
||||
} ;
|
||||
|
||||
typedef std::list<cChunkCoords> cChunkCoordsList;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Interface class used as a callback for operations that involve chunk coords
|
||||
class cChunkCoordCallback
|
||||
{
|
||||
public:
|
||||
virtual void Call(int a_ChunkX, int a_ChunkZ) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,300 +1,300 @@
|
||||
|
||||
// ChunkSender.cpp
|
||||
|
||||
// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Globals.h"
|
||||
#include "ChunkSender.h"
|
||||
#include "cWorld.h"
|
||||
#include "packets/cPacket_MapChunk.h"
|
||||
#include "packets/cPacket_PreChunk.h"
|
||||
#include "cBlockEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cNotifyChunkSender:
|
||||
|
||||
void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
m_ChunkSender->ChunkReady(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cChunkSender:
|
||||
|
||||
cChunkSender::cChunkSender(void) :
|
||||
super("ChunkSender"),
|
||||
m_World(NULL),
|
||||
m_Notify(NULL)
|
||||
{
|
||||
m_Notify.SetChunkSender(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkSender::~cChunkSender()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cChunkSender::Start(cWorld * a_World)
|
||||
{
|
||||
m_World = a_World;
|
||||
return super::Start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::Stop(void)
|
||||
{
|
||||
m_ShouldTerminate = true;
|
||||
m_evtQueue.Set();
|
||||
Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
|
||||
{
|
||||
// This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
m_ChunksReady.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
|
||||
}
|
||||
m_evtQueue.Set();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(a_Client != NULL);
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client)) != m_SendChunks.end())
|
||||
{
|
||||
// Already queued, bail out
|
||||
return;
|
||||
}
|
||||
m_SendChunks.push_back(sSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client));
|
||||
}
|
||||
m_evtQueue.Set();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::RemoveClient(cClientHandle * a_Client)
|
||||
{
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
for (sSendChunkList::iterator itr = m_SendChunks.begin(); itr != m_SendChunks.end();)
|
||||
{
|
||||
if (itr->m_Client == a_Client)
|
||||
{
|
||||
itr = m_SendChunks.erase(itr);
|
||||
continue;
|
||||
}
|
||||
++itr;
|
||||
} // for itr - m_SendChunks[]
|
||||
m_RemoveCount++;
|
||||
}
|
||||
m_evtQueue.Set();
|
||||
m_evtRemoved.Wait(); // Wait for removal confirmation
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::Execute(void)
|
||||
{
|
||||
while (!m_ShouldTerminate)
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
while (m_ChunksReady.empty() && m_SendChunks.empty())
|
||||
{
|
||||
int RemoveCount = m_RemoveCount;
|
||||
m_RemoveCount = 0;
|
||||
cCSUnlock Unlock(Lock);
|
||||
for (int i = 0; i < RemoveCount; i++)
|
||||
{
|
||||
m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted
|
||||
}
|
||||
m_evtQueue.Wait();
|
||||
if (m_ShouldTerminate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // while (empty)
|
||||
|
||||
if (!m_ChunksReady.empty())
|
||||
{
|
||||
// Take one from the queue:
|
||||
cChunkCoords Coords(m_ChunksReady.front());
|
||||
m_ChunksReady.pop_front();
|
||||
Lock.Unlock();
|
||||
|
||||
SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take one from the queue:
|
||||
sSendChunk Chunk(m_SendChunks.front());
|
||||
m_SendChunks.pop_front();
|
||||
Lock.Unlock();
|
||||
|
||||
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client);
|
||||
}
|
||||
Lock.Lock();
|
||||
int RemoveCount = m_RemoveCount;
|
||||
m_RemoveCount = 0;
|
||||
Lock.Unlock();
|
||||
for (int i = 0; i < RemoveCount; i++)
|
||||
{
|
||||
m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted
|
||||
}
|
||||
} // while (!mShouldTerminate)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(m_World != NULL);
|
||||
|
||||
// Ask the client if it still wants the chunk:
|
||||
if (a_Client != NULL)
|
||||
{
|
||||
if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the chunk has no clients, no need to packetize it:
|
||||
if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating
|
||||
if (!m_World->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the chunk is not lighted, queue it for relighting and get notified when it's ready:
|
||||
if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ))
|
||||
{
|
||||
m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare MapChunk packets:
|
||||
if( !m_World->GetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, *this) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
cPacket_PreChunk PreChunk(a_ChunkX, a_ChunkZ, true);
|
||||
cPacket_MapChunk MapChunk(a_ChunkX, a_ChunkY, a_ChunkZ, m_BlockData, m_BiomeMap);
|
||||
|
||||
// Send:
|
||||
if (a_Client == NULL)
|
||||
{
|
||||
m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, PreChunk);
|
||||
m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, MapChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Client->Send(PreChunk);
|
||||
a_Client->Send(MapChunk);
|
||||
}
|
||||
|
||||
// Send entity creation packets:
|
||||
for (PacketList::iterator itr = m_Packets.begin(); itr != m_Packets.end(); ++itr)
|
||||
{
|
||||
if (a_Client == NULL)
|
||||
{
|
||||
m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, **itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Client->Send(**itr);
|
||||
}
|
||||
delete *itr;
|
||||
} // for itr - m_Packets[]
|
||||
m_Packets.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::BlockEntity(cBlockEntity * a_Entity)
|
||||
{
|
||||
cPacket * Packet = a_Entity->GetPacket();
|
||||
if (Packet != NULL)
|
||||
{
|
||||
m_Packets.push_back(Packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::Entity(cEntity * a_Entity)
|
||||
{
|
||||
// Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
|
||||
{
|
||||
for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++)
|
||||
{
|
||||
if ((*a_BiomeMap)[i] < 255)
|
||||
{
|
||||
// Normal MC biome, copy as-is:
|
||||
m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: MCS-specific biome, need to map to some basic MC biome:
|
||||
ASSERT(!"Unimplemented MCS-specific biome");
|
||||
}
|
||||
} // for i - m_BiomeMap[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ChunkSender.cpp
|
||||
|
||||
// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Globals.h"
|
||||
#include "ChunkSender.h"
|
||||
#include "cWorld.h"
|
||||
#include "packets/cPacket_MapChunk.h"
|
||||
#include "packets/cPacket_PreChunk.h"
|
||||
#include "cBlockEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cNotifyChunkSender:
|
||||
|
||||
void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
m_ChunkSender->ChunkReady(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cChunkSender:
|
||||
|
||||
cChunkSender::cChunkSender(void) :
|
||||
super("ChunkSender"),
|
||||
m_World(NULL),
|
||||
m_Notify(NULL)
|
||||
{
|
||||
m_Notify.SetChunkSender(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkSender::~cChunkSender()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cChunkSender::Start(cWorld * a_World)
|
||||
{
|
||||
m_World = a_World;
|
||||
return super::Start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::Stop(void)
|
||||
{
|
||||
m_ShouldTerminate = true;
|
||||
m_evtQueue.Set();
|
||||
Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
|
||||
{
|
||||
// This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
m_ChunksReady.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
|
||||
}
|
||||
m_evtQueue.Set();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(a_Client != NULL);
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client)) != m_SendChunks.end())
|
||||
{
|
||||
// Already queued, bail out
|
||||
return;
|
||||
}
|
||||
m_SendChunks.push_back(sSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client));
|
||||
}
|
||||
m_evtQueue.Set();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::RemoveClient(cClientHandle * a_Client)
|
||||
{
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
for (sSendChunkList::iterator itr = m_SendChunks.begin(); itr != m_SendChunks.end();)
|
||||
{
|
||||
if (itr->m_Client == a_Client)
|
||||
{
|
||||
itr = m_SendChunks.erase(itr);
|
||||
continue;
|
||||
}
|
||||
++itr;
|
||||
} // for itr - m_SendChunks[]
|
||||
m_RemoveCount++;
|
||||
}
|
||||
m_evtQueue.Set();
|
||||
m_evtRemoved.Wait(); // Wait for removal confirmation
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::Execute(void)
|
||||
{
|
||||
while (!m_ShouldTerminate)
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
while (m_ChunksReady.empty() && m_SendChunks.empty())
|
||||
{
|
||||
int RemoveCount = m_RemoveCount;
|
||||
m_RemoveCount = 0;
|
||||
cCSUnlock Unlock(Lock);
|
||||
for (int i = 0; i < RemoveCount; i++)
|
||||
{
|
||||
m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted
|
||||
}
|
||||
m_evtQueue.Wait();
|
||||
if (m_ShouldTerminate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // while (empty)
|
||||
|
||||
if (!m_ChunksReady.empty())
|
||||
{
|
||||
// Take one from the queue:
|
||||
cChunkCoords Coords(m_ChunksReady.front());
|
||||
m_ChunksReady.pop_front();
|
||||
Lock.Unlock();
|
||||
|
||||
SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take one from the queue:
|
||||
sSendChunk Chunk(m_SendChunks.front());
|
||||
m_SendChunks.pop_front();
|
||||
Lock.Unlock();
|
||||
|
||||
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client);
|
||||
}
|
||||
Lock.Lock();
|
||||
int RemoveCount = m_RemoveCount;
|
||||
m_RemoveCount = 0;
|
||||
Lock.Unlock();
|
||||
for (int i = 0; i < RemoveCount; i++)
|
||||
{
|
||||
m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted
|
||||
}
|
||||
} // while (!mShouldTerminate)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(m_World != NULL);
|
||||
|
||||
// Ask the client if it still wants the chunk:
|
||||
if (a_Client != NULL)
|
||||
{
|
||||
if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the chunk has no clients, no need to packetize it:
|
||||
if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating
|
||||
if (!m_World->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the chunk is not lighted, queue it for relighting and get notified when it's ready:
|
||||
if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ))
|
||||
{
|
||||
m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare MapChunk packets:
|
||||
if( !m_World->GetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, *this) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
cPacket_PreChunk PreChunk(a_ChunkX, a_ChunkZ, true);
|
||||
cPacket_MapChunk MapChunk(a_ChunkX, a_ChunkY, a_ChunkZ, m_BlockData, m_BiomeMap);
|
||||
|
||||
// Send:
|
||||
if (a_Client == NULL)
|
||||
{
|
||||
m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, PreChunk);
|
||||
m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, MapChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Client->Send(PreChunk);
|
||||
a_Client->Send(MapChunk);
|
||||
}
|
||||
|
||||
// Send entity creation packets:
|
||||
for (PacketList::iterator itr = m_Packets.begin(); itr != m_Packets.end(); ++itr)
|
||||
{
|
||||
if (a_Client == NULL)
|
||||
{
|
||||
m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, **itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Client->Send(**itr);
|
||||
}
|
||||
delete *itr;
|
||||
} // for itr - m_Packets[]
|
||||
m_Packets.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::BlockEntity(cBlockEntity * a_Entity)
|
||||
{
|
||||
cPacket * Packet = a_Entity->GetPacket();
|
||||
if (Packet != NULL)
|
||||
{
|
||||
m_Packets.push_back(Packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::Entity(cEntity * a_Entity)
|
||||
{
|
||||
// Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
|
||||
{
|
||||
for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++)
|
||||
{
|
||||
if ((*a_BiomeMap)[i] < 255)
|
||||
{
|
||||
// Normal MC biome, copy as-is:
|
||||
m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: MCS-specific biome, need to map to some basic MC biome:
|
||||
ASSERT(!"Unimplemented MCS-specific biome");
|
||||
}
|
||||
} // for i - m_BiomeMap[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,153 +1,153 @@
|
||||
|
||||
// ChunkSender.h
|
||||
|
||||
// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients
|
||||
|
||||
/*
|
||||
The whole thing is a thread that runs in a loop, waiting for either:
|
||||
"finished chunks" (ChunkReady()), or
|
||||
"chunks to send" (QueueSendChunkTo() )
|
||||
to come to a queue.
|
||||
And once they do, it requests the chunk data and sends it all away, either
|
||||
broadcasting (ChunkReady), or
|
||||
sends to a specific client (QueueSendChunkTo)
|
||||
Chunk data is queried using the cChunkDataCallback interface.
|
||||
It is cached inside the ChunkSender object during the query and then processed after the query ends.
|
||||
Note that the data needs to be compressed only *after* the query finishes,
|
||||
because the query callbacks run with ChunkMap's CS locked.
|
||||
|
||||
A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient();
|
||||
this ensures that the client's Send() won't be called anymore by ChunkSender.
|
||||
Note that it may be called by world's BroadcastToChunk() if the client is still in the chunk.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cIsThread.h"
|
||||
#include "ChunkDef.h"
|
||||
#include "packets/cPacket.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cWorld;
|
||||
class cClientHandle;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class cChunkSender;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Callback that can be used to notify chunk sender upon another chunkcoord notification
|
||||
class cNotifyChunkSender :
|
||||
public cChunkCoordCallback
|
||||
{
|
||||
virtual void Call(int a_ChunkX, int a_ChunkZ) override;
|
||||
|
||||
cChunkSender * m_ChunkSender;
|
||||
public:
|
||||
cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {}
|
||||
|
||||
void SetChunkSender(cChunkSender * a_ChunkSender)
|
||||
{
|
||||
m_ChunkSender = a_ChunkSender;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cChunkSender:
|
||||
public cIsThread,
|
||||
public cChunkDataCollector
|
||||
{
|
||||
typedef cIsThread super;
|
||||
public:
|
||||
cChunkSender(void);
|
||||
~cChunkSender();
|
||||
|
||||
bool Start(cWorld * a_World);
|
||||
|
||||
void Stop(void);
|
||||
|
||||
/// Notifies that a chunk has become ready and it should be sent to all its clients
|
||||
void ChunkReady(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
|
||||
|
||||
/// Queues a chunk to be sent to a specific client
|
||||
void QueueSendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
|
||||
|
||||
/// Removes the a_Client from all waiting chunk send operations
|
||||
void RemoveClient(cClientHandle * a_Client);
|
||||
|
||||
protected:
|
||||
|
||||
/// Used for sending chunks to specific clients
|
||||
struct sSendChunk
|
||||
{
|
||||
int m_ChunkX;
|
||||
int m_ChunkY;
|
||||
int m_ChunkZ;
|
||||
cClientHandle * m_Client;
|
||||
|
||||
sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) :
|
||||
m_ChunkX(a_ChunkX),
|
||||
m_ChunkY(a_ChunkY),
|
||||
m_ChunkZ(a_ChunkZ),
|
||||
m_Client(a_Client)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator ==(const sSendChunk & a_Other)
|
||||
{
|
||||
return (
|
||||
(a_Other.m_ChunkX == m_ChunkX) &&
|
||||
(a_Other.m_ChunkY == m_ChunkY) &&
|
||||
(a_Other.m_ChunkZ == m_ChunkZ) &&
|
||||
(a_Other.m_Client == m_Client)
|
||||
);
|
||||
}
|
||||
};
|
||||
typedef std::list<sSendChunk> sSendChunkList;
|
||||
|
||||
cWorld * m_World;
|
||||
|
||||
cCriticalSection m_CS;
|
||||
cChunkCoordsList m_ChunksReady;
|
||||
sSendChunkList m_SendChunks;
|
||||
cEvent m_evtQueue; // Set when anything is added to m_ChunksReady
|
||||
cEvent m_evtRemoved; // Set when removed clients are safe to be deleted
|
||||
int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times)
|
||||
|
||||
cNotifyChunkSender m_Notify; // Used for chunks that don't have a valid lighting - they will be re-queued after lightcalc
|
||||
|
||||
// Data about the chunk that is being sent:
|
||||
// NOTE that m_BlockData[] is inherited from the cChunkDataCollector
|
||||
unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width];
|
||||
PacketList m_Packets; // Accumulator for the entity-packets to send
|
||||
|
||||
// cIsThread override:
|
||||
virtual void Execute(void) override;
|
||||
|
||||
// cChunkDataCollector overrides:
|
||||
// (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!)
|
||||
virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override;
|
||||
virtual void Entity (cEntity * a_Entity) override;
|
||||
virtual void BlockEntity (cBlockEntity * a_Entity) override;
|
||||
|
||||
/// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL
|
||||
void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ChunkSender.h
|
||||
|
||||
// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients
|
||||
|
||||
/*
|
||||
The whole thing is a thread that runs in a loop, waiting for either:
|
||||
"finished chunks" (ChunkReady()), or
|
||||
"chunks to send" (QueueSendChunkTo() )
|
||||
to come to a queue.
|
||||
And once they do, it requests the chunk data and sends it all away, either
|
||||
broadcasting (ChunkReady), or
|
||||
sends to a specific client (QueueSendChunkTo)
|
||||
Chunk data is queried using the cChunkDataCallback interface.
|
||||
It is cached inside the ChunkSender object during the query and then processed after the query ends.
|
||||
Note that the data needs to be compressed only *after* the query finishes,
|
||||
because the query callbacks run with ChunkMap's CS locked.
|
||||
|
||||
A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient();
|
||||
this ensures that the client's Send() won't be called anymore by ChunkSender.
|
||||
Note that it may be called by world's BroadcastToChunk() if the client is still in the chunk.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cIsThread.h"
|
||||
#include "ChunkDef.h"
|
||||
#include "packets/cPacket.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cWorld;
|
||||
class cClientHandle;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class cChunkSender;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Callback that can be used to notify chunk sender upon another chunkcoord notification
|
||||
class cNotifyChunkSender :
|
||||
public cChunkCoordCallback
|
||||
{
|
||||
virtual void Call(int a_ChunkX, int a_ChunkZ) override;
|
||||
|
||||
cChunkSender * m_ChunkSender;
|
||||
public:
|
||||
cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {}
|
||||
|
||||
void SetChunkSender(cChunkSender * a_ChunkSender)
|
||||
{
|
||||
m_ChunkSender = a_ChunkSender;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cChunkSender:
|
||||
public cIsThread,
|
||||
public cChunkDataCollector
|
||||
{
|
||||
typedef cIsThread super;
|
||||
public:
|
||||
cChunkSender(void);
|
||||
~cChunkSender();
|
||||
|
||||
bool Start(cWorld * a_World);
|
||||
|
||||
void Stop(void);
|
||||
|
||||
/// Notifies that a chunk has become ready and it should be sent to all its clients
|
||||
void ChunkReady(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
|
||||
|
||||
/// Queues a chunk to be sent to a specific client
|
||||
void QueueSendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
|
||||
|
||||
/// Removes the a_Client from all waiting chunk send operations
|
||||
void RemoveClient(cClientHandle * a_Client);
|
||||
|
||||
protected:
|
||||
|
||||
/// Used for sending chunks to specific clients
|
||||
struct sSendChunk
|
||||
{
|
||||
int m_ChunkX;
|
||||
int m_ChunkY;
|
||||
int m_ChunkZ;
|
||||
cClientHandle * m_Client;
|
||||
|
||||
sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) :
|
||||
m_ChunkX(a_ChunkX),
|
||||
m_ChunkY(a_ChunkY),
|
||||
m_ChunkZ(a_ChunkZ),
|
||||
m_Client(a_Client)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator ==(const sSendChunk & a_Other)
|
||||
{
|
||||
return (
|
||||
(a_Other.m_ChunkX == m_ChunkX) &&
|
||||
(a_Other.m_ChunkY == m_ChunkY) &&
|
||||
(a_Other.m_ChunkZ == m_ChunkZ) &&
|
||||
(a_Other.m_Client == m_Client)
|
||||
);
|
||||
}
|
||||
};
|
||||
typedef std::list<sSendChunk> sSendChunkList;
|
||||
|
||||
cWorld * m_World;
|
||||
|
||||
cCriticalSection m_CS;
|
||||
cChunkCoordsList m_ChunksReady;
|
||||
sSendChunkList m_SendChunks;
|
||||
cEvent m_evtQueue; // Set when anything is added to m_ChunksReady
|
||||
cEvent m_evtRemoved; // Set when removed clients are safe to be deleted
|
||||
int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times)
|
||||
|
||||
cNotifyChunkSender m_Notify; // Used for chunks that don't have a valid lighting - they will be re-queued after lightcalc
|
||||
|
||||
// Data about the chunk that is being sent:
|
||||
// NOTE that m_BlockData[] is inherited from the cChunkDataCollector
|
||||
unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width];
|
||||
PacketList m_Packets; // Accumulator for the entity-packets to send
|
||||
|
||||
// cIsThread override:
|
||||
virtual void Execute(void) override;
|
||||
|
||||
// cChunkDataCollector overrides:
|
||||
// (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!)
|
||||
virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override;
|
||||
virtual void Entity (cEntity * a_Entity) override;
|
||||
virtual void BlockEntity (cBlockEntity * a_Entity) override;
|
||||
|
||||
/// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL
|
||||
void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,427 +1,427 @@
|
||||
|
||||
// CompoGen.cpp
|
||||
|
||||
/* Implements the various terrain composition generators:
|
||||
- cCompoGenSameBlock
|
||||
- cCompoGenDebugBiomes
|
||||
- cCompoGenClassic
|
||||
*/
|
||||
|
||||
#include "Globals.h"
|
||||
#include "CompoGen.h"
|
||||
#include "BlockID.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenSameBlock:
|
||||
|
||||
void cCompoGenSameBlock::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Start;
|
||||
if (m_IsBedrocked)
|
||||
{
|
||||
a_BlockTypes[cChunkDef::MakeIndex(x, 0, z)] = E_BLOCK_BEDROCK;
|
||||
Start = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Start = 0;
|
||||
}
|
||||
for (int y = a_HeightMap[x + cChunkDef::Width * z]; y >= Start; y--)
|
||||
{
|
||||
a_BlockTypes[cChunkDef::MakeIndex(x, y, z)] = m_BlockType;
|
||||
} // for y
|
||||
} // for z
|
||||
} // for x
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenDebugBiomes:
|
||||
|
||||
void cCompoGenDebugBiomes::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
static BLOCKTYPE Blocks[] =
|
||||
{
|
||||
E_BLOCK_STONE,
|
||||
E_BLOCK_COBBLESTONE,
|
||||
E_BLOCK_LOG,
|
||||
E_BLOCK_PLANKS,
|
||||
E_BLOCK_SANDSTONE,
|
||||
E_BLOCK_WHITE_CLOTH,
|
||||
E_BLOCK_COAL_ORE,
|
||||
E_BLOCK_IRON_ORE,
|
||||
E_BLOCK_GOLD_ORE,
|
||||
E_BLOCK_DIAMOND_ORE,
|
||||
E_BLOCK_LAPIS_ORE,
|
||||
E_BLOCK_REDSTONE_ORE,
|
||||
E_BLOCK_IRON_BLOCK,
|
||||
E_BLOCK_GOLD_BLOCK,
|
||||
E_BLOCK_DIAMOND_BLOCK,
|
||||
E_BLOCK_LAPIS_BLOCK,
|
||||
E_BLOCK_BRICK,
|
||||
E_BLOCK_MOSSY_COBBLESTONE,
|
||||
E_BLOCK_OBSIDIAN,
|
||||
E_BLOCK_NETHERRACK,
|
||||
E_BLOCK_SOULSAND,
|
||||
E_BLOCK_NETHER_BRICK,
|
||||
E_BLOCK_BEDROCK,
|
||||
} ;
|
||||
|
||||
memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
BLOCKTYPE BlockType = Blocks[cChunkDef::GetBiome(a_BiomeMap, x, z) % ARRAYCOUNT(Blocks)];
|
||||
for (int y = a_HeightMap[x + cChunkDef::Width * z]; y >= 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockType);
|
||||
} // for y
|
||||
} // for z
|
||||
} // for x
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenClassic:
|
||||
|
||||
cCompoGenClassic::cCompoGenClassic(
|
||||
int a_SeaLevel, int a_BeachHeight, int a_BeachDepth,
|
||||
BLOCKTYPE a_BlockTop, BLOCKTYPE a_BlockMiddle, BLOCKTYPE a_BlockBottom,
|
||||
BLOCKTYPE a_BlockBeach, BLOCKTYPE a_BlockBeachBottom, BLOCKTYPE a_BlockSea
|
||||
) :
|
||||
m_SeaLevel(a_SeaLevel),
|
||||
m_BeachHeight(a_BeachHeight),
|
||||
m_BeachDepth(a_BeachDepth),
|
||||
m_BlockTop(a_BlockTop),
|
||||
m_BlockMiddle(a_BlockMiddle),
|
||||
m_BlockBottom(a_BlockBottom),
|
||||
m_BlockBeach(a_BlockBeach),
|
||||
m_BlockBeachBottom(a_BlockBeachBottom),
|
||||
m_BlockSea(a_BlockSea)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenClassic::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
/* The classic composition means:
|
||||
- 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
|
||||
- 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight
|
||||
- water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth
|
||||
- water from waterlevel, then 3 dirt, the rest stone otherwise
|
||||
- bedrock at the bottom
|
||||
*/
|
||||
|
||||
memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
|
||||
// The patterns to use for different situations, must be same length!
|
||||
static const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ;
|
||||
static const BLOCKTYPE PatternBeach[] = {m_BlockBeach, m_BlockBeach, m_BlockBeach, m_BlockBeachBottom} ;
|
||||
static const BLOCKTYPE PatternOcean[] = {m_BlockMiddle, m_BlockMiddle, m_BlockMiddle, m_BlockBottom} ;
|
||||
static int PatternLength = ARRAYCOUNT(PatternGround);
|
||||
ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach));
|
||||
ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean));
|
||||
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Height = cChunkDef::GetHeight(a_HeightMap, x, z);
|
||||
const BLOCKTYPE * Pattern;
|
||||
if (Height > m_SeaLevel + m_BeachHeight)
|
||||
{
|
||||
Pattern = PatternGround;
|
||||
}
|
||||
else if (Height > m_SeaLevel - m_BeachDepth)
|
||||
{
|
||||
Pattern = PatternBeach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Pattern = PatternOcean;
|
||||
}
|
||||
|
||||
// Fill water from sealevel down to height (if any):
|
||||
for (int y = m_SeaLevel; y >= Height; --y)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, m_BlockSea);
|
||||
}
|
||||
|
||||
// Fill from height till the bottom:
|
||||
for (int y = Height; y >= 1; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : m_BlockBottom);
|
||||
}
|
||||
|
||||
// The last layer is always bedrock:
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, 0, z, E_BLOCK_BEDROCK);
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenBiomal:
|
||||
|
||||
void cCompoGenBiomal::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
memset(a_BlockTypes, 0, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Height = cChunkDef::GetHeight(a_HeightMap, x, z);
|
||||
if (Height > m_SeaLevel)
|
||||
{
|
||||
switch (cChunkDef::GetBiome(a_BiomeMap, x, z))
|
||||
{
|
||||
case biOcean:
|
||||
case biPlains:
|
||||
case biExtremeHills:
|
||||
case biForest:
|
||||
case biTaiga:
|
||||
case biSwampland:
|
||||
case biRiver:
|
||||
case biFrozenOcean:
|
||||
case biFrozenRiver:
|
||||
case biIcePlains:
|
||||
case biIceMountains:
|
||||
case biForestHills:
|
||||
case biTaigaHills:
|
||||
case biExtremeHillsEdge:
|
||||
case biJungle:
|
||||
case biJungleHills:
|
||||
{
|
||||
FillColumnGrass(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
case biDesertHills:
|
||||
case biDesert:
|
||||
case biBeach:
|
||||
{
|
||||
FillColumnSand(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
case biMushroomIsland:
|
||||
case biMushroomShore:
|
||||
{
|
||||
FillColumnMycelium(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"CompoGenBiomal: Biome not implemented yet!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cChunkDef::GetBiome(a_BiomeMap, x, z))
|
||||
{
|
||||
case biDesert:
|
||||
case biBeach:
|
||||
{
|
||||
// Fill with water, sand, sandstone and stone
|
||||
FillColumnWaterSand(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Fill with water, sand/dirt/clay mix and stone
|
||||
FillColumnWaterMix(a_ChunkX, a_ChunkZ, x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
} // switch (biome)
|
||||
} // else (under water)
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, 0, z, E_BLOCK_BEDROCK);
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_GRASS,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_SAND,
|
||||
E_BLOCK_SAND,
|
||||
E_BLOCK_SAND,
|
||||
E_BLOCK_SANDSTONE,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_MYCELIUM,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
|
||||
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_WATER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnWaterMix(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
if (m_Noise.CubicNoise2D(0.5f * (cChunkDef::Width * a_ChunkX + a_RelX), 0.5f * (cChunkDef::Width * a_ChunkZ + a_RelZ)) < 0)
|
||||
{
|
||||
FillColumnWaterSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dirt
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_WATER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize)
|
||||
{
|
||||
for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// CompoGen.cpp
|
||||
|
||||
/* Implements the various terrain composition generators:
|
||||
- cCompoGenSameBlock
|
||||
- cCompoGenDebugBiomes
|
||||
- cCompoGenClassic
|
||||
*/
|
||||
|
||||
#include "Globals.h"
|
||||
#include "CompoGen.h"
|
||||
#include "BlockID.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenSameBlock:
|
||||
|
||||
void cCompoGenSameBlock::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Start;
|
||||
if (m_IsBedrocked)
|
||||
{
|
||||
a_BlockTypes[cChunkDef::MakeIndex(x, 0, z)] = E_BLOCK_BEDROCK;
|
||||
Start = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Start = 0;
|
||||
}
|
||||
for (int y = a_HeightMap[x + cChunkDef::Width * z]; y >= Start; y--)
|
||||
{
|
||||
a_BlockTypes[cChunkDef::MakeIndex(x, y, z)] = m_BlockType;
|
||||
} // for y
|
||||
} // for z
|
||||
} // for x
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenDebugBiomes:
|
||||
|
||||
void cCompoGenDebugBiomes::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
static BLOCKTYPE Blocks[] =
|
||||
{
|
||||
E_BLOCK_STONE,
|
||||
E_BLOCK_COBBLESTONE,
|
||||
E_BLOCK_LOG,
|
||||
E_BLOCK_PLANKS,
|
||||
E_BLOCK_SANDSTONE,
|
||||
E_BLOCK_WHITE_CLOTH,
|
||||
E_BLOCK_COAL_ORE,
|
||||
E_BLOCK_IRON_ORE,
|
||||
E_BLOCK_GOLD_ORE,
|
||||
E_BLOCK_DIAMOND_ORE,
|
||||
E_BLOCK_LAPIS_ORE,
|
||||
E_BLOCK_REDSTONE_ORE,
|
||||
E_BLOCK_IRON_BLOCK,
|
||||
E_BLOCK_GOLD_BLOCK,
|
||||
E_BLOCK_DIAMOND_BLOCK,
|
||||
E_BLOCK_LAPIS_BLOCK,
|
||||
E_BLOCK_BRICK,
|
||||
E_BLOCK_MOSSY_COBBLESTONE,
|
||||
E_BLOCK_OBSIDIAN,
|
||||
E_BLOCK_NETHERRACK,
|
||||
E_BLOCK_SOULSAND,
|
||||
E_BLOCK_NETHER_BRICK,
|
||||
E_BLOCK_BEDROCK,
|
||||
} ;
|
||||
|
||||
memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
BLOCKTYPE BlockType = Blocks[cChunkDef::GetBiome(a_BiomeMap, x, z) % ARRAYCOUNT(Blocks)];
|
||||
for (int y = a_HeightMap[x + cChunkDef::Width * z]; y >= 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockType);
|
||||
} // for y
|
||||
} // for z
|
||||
} // for x
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenClassic:
|
||||
|
||||
cCompoGenClassic::cCompoGenClassic(
|
||||
int a_SeaLevel, int a_BeachHeight, int a_BeachDepth,
|
||||
BLOCKTYPE a_BlockTop, BLOCKTYPE a_BlockMiddle, BLOCKTYPE a_BlockBottom,
|
||||
BLOCKTYPE a_BlockBeach, BLOCKTYPE a_BlockBeachBottom, BLOCKTYPE a_BlockSea
|
||||
) :
|
||||
m_SeaLevel(a_SeaLevel),
|
||||
m_BeachHeight(a_BeachHeight),
|
||||
m_BeachDepth(a_BeachDepth),
|
||||
m_BlockTop(a_BlockTop),
|
||||
m_BlockMiddle(a_BlockMiddle),
|
||||
m_BlockBottom(a_BlockBottom),
|
||||
m_BlockBeach(a_BlockBeach),
|
||||
m_BlockBeachBottom(a_BlockBeachBottom),
|
||||
m_BlockSea(a_BlockSea)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenClassic::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
/* The classic composition means:
|
||||
- 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
|
||||
- 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight
|
||||
- water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth
|
||||
- water from waterlevel, then 3 dirt, the rest stone otherwise
|
||||
- bedrock at the bottom
|
||||
*/
|
||||
|
||||
memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
|
||||
// The patterns to use for different situations, must be same length!
|
||||
static const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ;
|
||||
static const BLOCKTYPE PatternBeach[] = {m_BlockBeach, m_BlockBeach, m_BlockBeach, m_BlockBeachBottom} ;
|
||||
static const BLOCKTYPE PatternOcean[] = {m_BlockMiddle, m_BlockMiddle, m_BlockMiddle, m_BlockBottom} ;
|
||||
static int PatternLength = ARRAYCOUNT(PatternGround);
|
||||
ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach));
|
||||
ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean));
|
||||
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Height = cChunkDef::GetHeight(a_HeightMap, x, z);
|
||||
const BLOCKTYPE * Pattern;
|
||||
if (Height > m_SeaLevel + m_BeachHeight)
|
||||
{
|
||||
Pattern = PatternGround;
|
||||
}
|
||||
else if (Height > m_SeaLevel - m_BeachDepth)
|
||||
{
|
||||
Pattern = PatternBeach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Pattern = PatternOcean;
|
||||
}
|
||||
|
||||
// Fill water from sealevel down to height (if any):
|
||||
for (int y = m_SeaLevel; y >= Height; --y)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, m_BlockSea);
|
||||
}
|
||||
|
||||
// Fill from height till the bottom:
|
||||
for (int y = Height; y >= 1; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : m_BlockBottom);
|
||||
}
|
||||
|
||||
// The last layer is always bedrock:
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, 0, z, E_BLOCK_BEDROCK);
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCompoGenBiomal:
|
||||
|
||||
void cCompoGenBiomal::ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
)
|
||||
{
|
||||
memset(a_BlockTypes, 0, sizeof(a_BlockTypes));
|
||||
memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int Height = cChunkDef::GetHeight(a_HeightMap, x, z);
|
||||
if (Height > m_SeaLevel)
|
||||
{
|
||||
switch (cChunkDef::GetBiome(a_BiomeMap, x, z))
|
||||
{
|
||||
case biOcean:
|
||||
case biPlains:
|
||||
case biExtremeHills:
|
||||
case biForest:
|
||||
case biTaiga:
|
||||
case biSwampland:
|
||||
case biRiver:
|
||||
case biFrozenOcean:
|
||||
case biFrozenRiver:
|
||||
case biIcePlains:
|
||||
case biIceMountains:
|
||||
case biForestHills:
|
||||
case biTaigaHills:
|
||||
case biExtremeHillsEdge:
|
||||
case biJungle:
|
||||
case biJungleHills:
|
||||
{
|
||||
FillColumnGrass(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
case biDesertHills:
|
||||
case biDesert:
|
||||
case biBeach:
|
||||
{
|
||||
FillColumnSand(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
case biMushroomIsland:
|
||||
case biMushroomShore:
|
||||
{
|
||||
FillColumnMycelium(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"CompoGenBiomal: Biome not implemented yet!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cChunkDef::GetBiome(a_BiomeMap, x, z))
|
||||
{
|
||||
case biDesert:
|
||||
case biBeach:
|
||||
{
|
||||
// Fill with water, sand, sandstone and stone
|
||||
FillColumnWaterSand(x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Fill with water, sand/dirt/clay mix and stone
|
||||
FillColumnWaterMix(a_ChunkX, a_ChunkZ, x, z, Height, a_BlockTypes);
|
||||
break;
|
||||
}
|
||||
} // switch (biome)
|
||||
} // else (under water)
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, 0, z, E_BLOCK_BEDROCK);
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_GRASS,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_SAND,
|
||||
E_BLOCK_SAND,
|
||||
E_BLOCK_SAND,
|
||||
E_BLOCK_SANDSTONE,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_MYCELIUM,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
|
||||
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_WATER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnWaterMix(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
|
||||
{
|
||||
if (m_Noise.CubicNoise2D(0.5f * (cChunkDef::Width * a_ChunkX + a_RelX), 0.5f * (cChunkDef::Width * a_ChunkZ + a_RelZ)) < 0)
|
||||
{
|
||||
FillColumnWaterSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dirt
|
||||
BLOCKTYPE Pattern[] =
|
||||
{
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
E_BLOCK_DIRT,
|
||||
} ;
|
||||
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
|
||||
|
||||
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
|
||||
}
|
||||
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_WATER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize)
|
||||
{
|
||||
for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++)
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,154 +1,154 @@
|
||||
|
||||
// CompoGen.h
|
||||
|
||||
/* Interfaces to the various terrain composition generators:
|
||||
- cCompoGenSameBlock
|
||||
- cCompoGenDebugBiomes
|
||||
- cCompoGenClassic
|
||||
- cCompoGenBiomal
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cChunkGenerator.h"
|
||||
#include "cNoise.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cCompoGenSameBlock :
|
||||
public cTerrainCompositionGen
|
||||
{
|
||||
public:
|
||||
cCompoGenSameBlock(BLOCKTYPE a_BlockType, bool a_IsBedrocked) :
|
||||
m_BlockType(a_BlockType),
|
||||
m_IsBedrocked(a_IsBedrocked)
|
||||
{}
|
||||
|
||||
protected:
|
||||
|
||||
BLOCKTYPE m_BlockType;
|
||||
bool m_IsBedrocked;
|
||||
|
||||
// cTerrainCompositionGen overrides:
|
||||
virtual void ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cCompoGenDebugBiomes :
|
||||
public cTerrainCompositionGen
|
||||
{
|
||||
public:
|
||||
cCompoGenDebugBiomes(void) {}
|
||||
|
||||
protected:
|
||||
|
||||
// cTerrainCompositionGen overrides:
|
||||
virtual void ComposeTerrain(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
|
||||
cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
|
||||
const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
|
||||
const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
|
||||
cEntityList & a_Entities, // Entitites may be generated along with the terrain
|
||||
cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
|
||||
) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cCompoGenClassic :
|
||||
public cTerrainCompositionGen
|
||||
{
|
||||
public:
|
||||
cCompoGenClassic(
|
||||
int a_SeaLevel, int a_BeachHeight, int a_BeachDepth,
|
||||
BLOCKTYPE a_BlockTop, BLOCKTYPE a_BlockMiddle, BLOCKTYPE a_BlockBottom,
|
||||
BLOCKTYPE a_BlockBeach, BLOCKTYPE a_BlockBeachBottom, BLOCKTYPE a_BlockSea
|
||||
);
|
||||
|
||||
protected:
|
||||
|
||||
int m_SeaLevel;
|
||||
int m_BeachHeight;
|
||||
int m_BeachDepth;
|
||||
BLOCKTYPE m_BlockTop;
|
||||
BLOCKTYPE | ||||