1
0

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:
cedeel@gmail.com
2012-06-14 13:06:06 +00:00
parent 0b29b3b3cf
commit 92c59963f8
390 changed files with 86875 additions and 86875 deletions

View File

@@ -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"

View File

@@ -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);
}

View File

@@ -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_);
}

View File

@@ -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;
}
}

View File

@@ -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(&ltime);
tm* gmt= gmtime(&ltime);
#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(&ltime);
tm* gmt= gmtime(&ltime);
#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;
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 );
};

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
} ;

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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;
} ;

View File

@@ -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[]
}

View File

@@ -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);
} ;

View File

@@ -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]);
}
}

View File

@@ -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