aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2011-05-21 01:11:58 +0300
committerPerttu Ahola <celeron55@gmail.com>2011-05-21 01:11:58 +0300
commit76e241392d2ad232b751c74df7cba9e5acfa7855 (patch)
treee58f72b379c1a4e1b86a4e85c6c0007109635b32
parent73a18b9271bebddfe6716529bb13cad30869bc16 (diff)
parent52d857cf1527948e1db5eead457e51b6741de8a2 (diff)
downloadminetest-76e241392d2ad232b751c74df7cba9e5acfa7855.tar.gz
minetest-76e241392d2ad232b751c74df7cba9e5acfa7855.tar.bz2
minetest-76e241392d2ad232b751c74df7cba9e5acfa7855.zip
Merge: New map directory structure and player passwords
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/base64.cpp123
-rw-r--r--src/base64.h4
-rw-r--r--src/client.cpp21
-rw-r--r--src/client.h9
-rw-r--r--src/clientserver.h6
-rw-r--r--src/filesys.cpp19
-rw-r--r--src/filesys.h3
-rw-r--r--src/game.cpp22
-rw-r--r--src/game.h1
-rw-r--r--src/guiMainMenu.cpp45
-rw-r--r--src/guiMainMenu.h1
-rw-r--r--src/main.cpp28
-rw-r--r--src/map.cpp124
-rw-r--r--src/map.h12
-rw-r--r--src/player.cpp6
-rw-r--r--src/player.h13
-rw-r--r--src/server.cpp59
-rw-r--r--src/server.h6
-rw-r--r--src/sha1.cpp207
-rw-r--r--src/sha1.h51
21 files changed, 680 insertions, 82 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 74396cee9..58596a101 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -95,6 +95,8 @@ set(minetest_SRCS
tile.cpp
game.cpp
main.cpp
+ sha1.cpp
+ base64.cpp
)
# Server sources
diff --git a/src/base64.cpp b/src/base64.cpp
new file mode 100644
index 000000000..2a863d161
--- /dev/null
+++ b/src/base64.cpp
@@ -0,0 +1,123 @@
+/*
+ base64.cpp and base64.h
+
+ Copyright (C) 2004-2008 René 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é Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+#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;
+}
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 000000000..65d5db8b2
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,4 @@
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
diff --git a/src/client.cpp b/src/client.cpp
index ce862a002..e2cda97c1 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -68,6 +68,7 @@ void * MeshUpdateThread::Thread()
Client::Client(
IrrlichtDevice *device,
const char *playername,
+ std::string password,
MapDrawControl &control):
m_mesh_update_thread(),
m_env(
@@ -83,7 +84,9 @@ Client::Client(
m_server_ser_ver(SER_FMT_VER_INVALID),
m_inventory_updated(false),
m_time_of_day(0),
- m_map_seed(0)
+ m_map_seed(0),
+ m_password(password),
+ m_access_denied(false)
{
m_packetcounter_timer = 0.0;
m_delete_unused_sectors_timer = 0.0;
@@ -299,11 +302,14 @@ void Client::step(float dtime)
// [0] u16 TOSERVER_INIT
// [2] u8 SER_FMT_VER_HIGHEST
// [3] u8[20] player_name
- SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE);
+ // [23] u8[28] password
+ SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
writeU16(&data[0], TOSERVER_INIT);
writeU8(&data[2], SER_FMT_VER_HIGHEST);
memset((char*)&data[3], 0, PLAYERNAME_SIZE);
snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
+ snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
+
// Send as unreliable
Send(0, data, false);
}
@@ -597,7 +603,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
return;
}
-
+
+ if(command == TOCLIENT_ACCESS_DENIED)
+ {
+ // The server didn't like our password. Note, this needs
+ // to be processed even if the serialisation format has
+ // not been agreed yet, the same as TOCLIENT_INIT.
+ m_access_denied = true;
+ return;
+ }
+
if(ser_version == SER_FMT_VER_INVALID)
{
dout_client<<DTIME<<"WARNING: Client: Server serialization"
diff --git a/src/client.h b/src/client.h
index ee73cc42c..222f83ab7 100644
--- a/src/client.h
+++ b/src/client.h
@@ -207,6 +207,7 @@ public:
Client(
IrrlichtDevice *device,
const char *playername,
+ std::string password,
MapDrawControl &control
);
@@ -377,6 +378,11 @@ public:
// Get event from queue. CE_NONE is returned if queue is empty.
ClientEvent getClientEvent();
+ inline bool accessDenied()
+ {
+ return m_access_denied;
+ }
+
private:
// Virtual methods from con::PeerHandler
@@ -430,6 +436,9 @@ private:
// The seed returned by the server in TOCLIENT_INIT is stored here
u64 m_map_seed;
+ std::string m_password;
+ bool m_access_denied;
+
InventoryContext m_inventory_context;
Queue<ClientEvent> m_client_event_queue;
diff --git a/src/clientserver.h b/src/clientserver.h
index 46ffa5eab..a64a11f08 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -150,6 +150,11 @@ enum ToClientCommand
f1000 player pitch
f1000 player yaw
*/
+
+ TOCLIENT_ACCESS_DENIED = 0x35,
+ /*
+ u16 command
+ */
};
enum ToServerCommand
@@ -161,6 +166,7 @@ enum ToServerCommand
[0] u16 TOSERVER_INIT
[2] u8 SER_FMT_VER_HIGHEST
[3] u8[20] player_name
+ [23] u8[28] password
*/
TOSERVER_INIT2 = 0x11,
diff --git a/src/filesys.cpp b/src/filesys.cpp
index 287090e8a..8248a13d4 100644
--- a/src/filesys.cpp
+++ b/src/filesys.cpp
@@ -290,5 +290,24 @@ bool RecursiveDeleteContent(std::string path)
return true;
}
+bool CreateAllDirs(std::string path)
+{
+
+ size_t pos;
+ std::vector<std::string> tocreate;
+ std::string basepath = path;
+ while(!PathExists(basepath))
+ {
+ tocreate.push_back(basepath);
+ pos = basepath.rfind('/');
+ if(pos == std::string::npos)
+ return false;
+ basepath = basepath.substr(0,pos);
+ }
+ for(int i=tocreate.size()-1;i>=0;i--)
+ CreateDir(tocreate[i]);
+ return true;
+}
+
} // namespace fs
diff --git a/src/filesys.h b/src/filesys.h
index 4dd90b84e..b74b34f3d 100644
--- a/src/filesys.h
+++ b/src/filesys.h
@@ -38,6 +38,9 @@ std::vector<DirListNode> GetDirListing(std::string path);
// Returns true if already exists
bool CreateDir(std::string path);
+// Create all directories on the given path that don't already exist.
+bool CreateAllDirs(std::string path);
+
bool PathExists(std::string path);
// Only pass full paths to this one. True on success.
diff --git a/src/game.cpp b/src/game.cpp
index d90262060..69e673fa4 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -634,6 +634,7 @@ void the_game(
gui::IGUIFont* font,
std::string map_dir,
std::string playername,
+ std::string password,
std::string address,
u16 port,
std::wstring &error_message
@@ -672,7 +673,6 @@ void the_game(
guienv->drawAll();
driver->endScene();
- std::cout<<DTIME<<"Creating server and client"<<std::endl;
/*
Create server.
@@ -680,6 +680,7 @@ void the_game(
*/
SharedPtr<Server> server;
if(address == ""){
+ std::cout<<DTIME<<"Creating server"<<std::endl;
server = new Server(map_dir);
server->start(port);
}
@@ -688,7 +689,8 @@ void the_game(
Create client
*/
- Client client(device, playername.c_str(), draw_control);
+ std::cout<<DTIME<<"Creating client"<<std::endl;
+ Client client(device, playername.c_str(), password, draw_control);
Address connect_address(0,0,0,0, port);
try{
@@ -727,6 +729,10 @@ void the_game(
could_connect = true;
break;
}
+ if(client.accessDenied())
+ {
+ break;
+ }
// Wait for 10 seconds
if(time_counter >= 10.0)
{
@@ -755,8 +761,16 @@ void the_game(
if(could_connect == false)
{
- std::cout<<DTIME<<"Timed out."<<std::endl;
- error_message = L"Connection timed out.";
+ if(client.accessDenied())
+ {
+ error_message = L"Access denied. Check your password and try again.";
+ std::cout<<DTIME<<"Access denied."<<std::endl;
+ }
+ else
+ {
+ error_message = L"Connection timed out.";
+ std::cout<<DTIME<<"Timed out."<<std::endl;
+ }
gui_loadingtext->remove();
return;
}
diff --git a/src/game.h b/src/game.h
index 7cba1299e..eb289b8f2 100644
--- a/src/game.h
+++ b/src/game.h
@@ -67,6 +67,7 @@ void the_game(
gui::IGUIFont* font,
std::string map_dir,
std::string playername,
+ std::string password,
std::string address,
u16 port,
std::wstring &error_message
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
index ac02f79e3..a30e006a6 100644
--- a/src/guiMainMenu.cpp
+++ b/src/guiMainMenu.cpp
@@ -164,30 +164,38 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
- // Nickname
+ // Nickname + password
{
- core::rect<s32> rect(0, 0, 100, 20);
- rect += topleft_client + v2s32(40, 50+6);
- const wchar_t *text = L"Nickname";
+ core::rect<s32> rect(0, 0, 110, 20);
+ rect += topleft_client + v2s32(35, 50+6);
+ const wchar_t *text = L"Name/Password";
Environment->addStaticText(text, rect, false, true, this, -1);
}
{
- core::rect<s32> rect(0, 0, 250, 30);
+ core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, 50);
gui::IGUIElement *e =
Environment->addEditBox(text_name.c_str(), rect, true, this, 258);
if(text_name == L"")
Environment->setFocus(e);
}
+ {
+ core::rect<s32> rect(0, 0, 120, 30);
+ rect += topleft_client + v2s32(size_client.X-60-100, 50);
+ gui::IGUIEditBox *e =
+ Environment->addEditBox(L"", rect, true, this, 264);
+ e->setPasswordBox(true);
+
+ }
// Address + port
{
- core::rect<s32> rect(0, 0, 100, 20);
- rect += topleft_client + v2s32(40, 100+6);
- const wchar_t *text = L"Address + Port";
+ core::rect<s32> rect(0, 0, 110, 20);
+ rect += topleft_client + v2s32(35, 100+6);
+ const wchar_t *text = L"Address/Port";
Environment->addStaticText(text, rect, false, true, this, -1);
}
{
- core::rect<s32> rect(0, 0, 250, 30);
+ core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, 100);
gui::IGUIElement *e =
Environment->addEditBox(text_address.c_str(), rect, true, this, 256);
@@ -195,9 +203,9 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
Environment->setFocus(e);
}
{
- core::rect<s32> rect(0, 0, 100, 30);
+ core::rect<s32> rect(0, 0, 120, 30);
//rect += topleft_client + v2s32(160+250+20, 125);
- rect += topleft_client + v2s32(size_client.X-40-100, 100);
+ rect += topleft_client + v2s32(size_client.X-60-100, 100);
Environment->addEditBox(text_port.c_str(), rect, true, this, 257);
}
{
@@ -208,13 +216,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
}
{
core::rect<s32> rect(0, 0, 250, 30);
- rect += topleft_client + v2s32(40, 150);
+ rect += topleft_client + v2s32(35, 150);
Environment->addCheckBox(fancy_trees, rect, this, 263,
L"Fancy trees");
}
{
core::rect<s32> rect(0, 0, 250, 30);
- rect += topleft_client + v2s32(40, 150+30);
+ rect += topleft_client + v2s32(35, 150+30);
Environment->addCheckBox(smooth_lighting, rect, this, 262,
L"Smooth Lighting");
}
@@ -245,12 +253,12 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
// Server parameters
{
core::rect<s32> rect(0, 0, 250, 30);
- rect += topleft_server + v2s32(40, 30);
+ rect += topleft_server + v2s32(35, 30);
Environment->addCheckBox(creative_mode, rect, this, 259, L"Creative Mode");
}
{
core::rect<s32> rect(0, 0, 250, 30);
- rect += topleft_server + v2s32(40, 60);
+ rect += topleft_server + v2s32(35, 60);
Environment->addCheckBox(enable_damage, rect, this, 261, L"Enable Damage");
}
// Map delete button
@@ -297,6 +305,11 @@ void GUIMainMenu::acceptInput()
m_data->name = e->getText();
}
{
+ gui::IGUIElement *e = getElementFromId(264);
+ if(e != NULL)
+ m_data->password = e->getText();
+ }
+ {
gui::IGUIElement *e = getElementFromId(256);
if(e != NULL)
m_data->address = e->getText();
@@ -380,7 +393,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
{
switch(event.GUIEvent.Caller->getID())
{
- case 256: case 257: case 258:
+ case 256: case 257: case 258: case 264:
acceptInput();
quitMenu();
return true;
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index 4999d68ba..edd519024 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -46,6 +46,7 @@ struct MainMenuData
std::wstring address;
std::wstring port;
std::wstring name;
+ std::wstring password;
bool fancy_trees;
bool smooth_lighting;
// Server options
diff --git a/src/main.cpp b/src/main.cpp
index 5d607b2d8..184643b9f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -325,6 +325,8 @@ Making it more portable:
#include "materials.h"
#include "game.h"
#include "keycode.h"
+#include "sha1.h"
+#include "base64.h"
// This makes textures
ITextureSource *g_texturesource = NULL;
@@ -1170,6 +1172,7 @@ int main(int argc, char *argv[])
return 0;
}
+
/*
More parameters
*/
@@ -1324,11 +1327,15 @@ int main(int argc, char *argv[])
*/
std::wstring error_message = L"";
+ // The password entered during the menu screen,
+ std::string password;
+
/*
Menu-game loop
*/
while(device->run() && kill == false)
{
+
// This is used for catching disconnects
try
{
@@ -1428,6 +1435,26 @@ int main(int argc, char *argv[])
}
playername = wide_to_narrow(menudata.name);
+
+ // Get an sha-1 hash of the player's name combined with
+ // the password entered. That's what the server uses as
+ // their password. (Exception : if the password field is
+ // blank, we send a blank password - this is for backwards
+ // compatibility with password-less players).
+ if(menudata.password.length() > 0)
+ {
+ std::string slt=playername + wide_to_narrow(menudata.password);
+ SHA1 *sha1 = new SHA1();
+ sha1->addBytes(slt.c_str(), slt.length());
+ unsigned char *digest = sha1->getDigest();
+ password = base64_encode(digest, 20);
+ free(digest);
+ }
+ else
+ {
+ password = "";
+ }
+
address = wide_to_narrow(menudata.address);
int newport = stoi(wide_to_narrow(menudata.port));
if(newport != 0)
@@ -1474,6 +1501,7 @@ int main(int argc, char *argv[])
font,
map_dir,
playername,
+ password,
address,
port,
error_message
diff --git a/src/map.cpp b/src/map.cpp
index 63f01ddee..cd2ba9154 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -4875,9 +4875,9 @@ plan_b:
return (s16)level;
}
-void ServerMap::createDir(std::string path)
+void ServerMap::createDirs(std::string path)
{
- if(fs::CreateDir(path) == false)
+ if(fs::CreateAllDirs(path) == false)
{
m_dout<<DTIME<<"ServerMap: Failed to create directory "
<<"\""<<path<<"\""<<std::endl;
@@ -4885,29 +4885,52 @@ void ServerMap::createDir(std::string path)
}
}
-std::string ServerMap::getSectorSubDir(v2s16 pos)
+std::string ServerMap::getSectorDir(v2s16 pos, int layout)
{
char cc[9];
- snprintf(cc, 9, "%.4x%.4x",
- (unsigned int)pos.X&0xffff,
- (unsigned int)pos.Y&0xffff);
+ switch(layout)
+ {
+ case 1:
+ snprintf(cc, 9, "%.4x%.4x",
+ (unsigned int)pos.X&0xffff,
+ (unsigned int)pos.Y&0xffff);
- return std::string(cc);
-}
+ return m_savedir + "/sectors/" + cc;
+ case 2:
+ snprintf(cc, 9, "%.3x/%.3x",
+ (unsigned int)pos.X&0xfff,
+ (unsigned int)pos.Y&0xfff);
-std::string ServerMap::getSectorDir(v2s16 pos)
-{
- return m_savedir + "/sectors/" + getSectorSubDir(pos);
+ return m_savedir + "/sectors2/" + cc;
+ default:
+ assert(false);
+ }
}
v2s16 ServerMap::getSectorPos(std::string dirname)
{
- if(dirname.size() != 8)
- throw InvalidFilenameException("Invalid sector directory name");
unsigned int x, y;
- int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
- if(r != 2)
- throw InvalidFilenameException("Invalid sector directory name");
+ int r;
+ size_t spos = dirname.rfind('/') + 1;
+ assert(spos != std::string::npos);
+ if(dirname.size() - spos == 8)
+ {
+ // Old layout
+ r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
+ }
+ else if(dirname.size() - spos == 3)
+ {
+ // New layout
+ r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
+ // Sign-extend the 12 bit values up to 16 bits...
+ if(x&0x800) x|=0xF000;
+ if(y&0x800) y|=0xF000;
+ }
+ else
+ {
+ assert(false);
+ }
+ assert(r == 2);
v2s16 pos((s16)x, (s16)y);
return pos;
}
@@ -5106,7 +5129,7 @@ void ServerMap::saveMapMeta()
<<"seed="<<m_seed<<", chunksize="<<m_chunksize
<<std::endl;
- createDir(m_savedir);
+ createDirs(m_savedir);
std::string fullpath = m_savedir + "/map_meta.txt";
std::ofstream os(fullpath.c_str(), std::ios_base::binary);
@@ -5179,7 +5202,7 @@ void ServerMap::saveChunkMeta()
dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
<<count<<" chunks"<<std::endl;
- createDir(m_savedir);
+ createDirs(m_savedir);
std::string fullpath = m_savedir + "/chunk_meta";
std::ofstream os(fullpath.c_str(), std::ios_base::binary);
@@ -5267,10 +5290,8 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
u8 version = SER_FMT_VER_HIGHEST;
// Get destination
v2s16 pos = sector->getPos();
- createDir(m_savedir);
- createDir(m_savedir+"/sectors");
std::string dir = getSectorDir(pos);
- createDir(dir);
+ createDirs(dir);
std::string fullpath = dir + "/meta";
std::ofstream o(fullpath.c_str(), std::ios_base::binary);
@@ -5282,22 +5303,21 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
sector->differs_from_disk = false;
}
-MapSector* ServerMap::loadSectorMeta(std::string dirname)
+MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
{
DSTACK(__FUNCTION_NAME);
// Get destination
- v2s16 p2d = getSectorPos(dirname);
- std::string dir = m_savedir + "/sectors/" + dirname;
+ v2s16 p2d = getSectorPos(sectordir);
ServerMapSector *sector = NULL;
-
- std::string fullpath = dir + "/meta";
+
+ std::string fullpath = sectordir + "/meta";
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
if(is.good() == false)
{
// If the directory exists anyway, it probably is in some old
// format. Just go ahead and create the sector.
- if(fs::PathExists(dir))
+ if(fs::PathExists(sectordir))
{
dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
<<fullpath<<" doesn't exist but directory does."
@@ -5307,12 +5327,16 @@ MapSector* ServerMap::loadSectorMeta(std::string dirname)
m_sectors.insert(p2d, sector);
}
else
+ {
throw FileNotGoodException("Cannot open sector metafile");
+ }
}
else
{
sector = ServerMapSector::deSerialize
(is, this, p2d, m_sectors);
+ if(save_after_load)
+ saveSectorMeta(sector);
}
sector->differs_from_disk = false;
@@ -5323,14 +5347,31 @@ MapSector* ServerMap::loadSectorMeta(std::string dirname)
bool ServerMap::loadSectorFull(v2s16 p2d)
{
DSTACK(__FUNCTION_NAME);
- std::string sectorsubdir = getSectorSubDir(p2d);
MapSector *sector = NULL;
+ // The directory layout we're going to load from.
+ // 1 - original sectors/xxxxzzzz/
+ // 2 - new sectors2/xxx/zzz/
+ // If we load from anything but the latest structure, we will
+ // immediately save to the new one, and remove the old.
+ int loadlayout = 1;
+ std::string sectordir1 = getSectorDir(p2d, 1);
+ std::string sectordir;
+ if(fs::PathExists(sectordir1))
+ {
+ sectordir = sectordir1;
+ }
+ else
+ {
+ loadlayout = 2;
+ sectordir = getSectorDir(p2d, 2);
+ }
+
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
try{
- sector = loadSectorMeta(sectorsubdir);
+ sector = loadSectorMeta(sectordir, loadlayout != 2);
}
catch(InvalidFilenameException &e)
{
@@ -5349,7 +5390,7 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
Load blocks
*/
std::vector<fs::DirListNode> list2 = fs::GetDirListing
- (m_savedir+"/sectors/"+sectorsubdir);
+ (sectordir);
std::vector<fs::DirListNode>::iterator i2;
for(i2=list2.begin(); i2!=list2.end(); i2++)
{
@@ -5357,16 +5398,25 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
if(i2->dir)
continue;
try{
- loadBlock(sectorsubdir, i2->name, sector);
+ loadBlock(sectordir, i2->name, sector, loadlayout != 2);
}
catch(InvalidFilenameException &e)
{
// This catches unknown crap in directory
}
}
+
+ if(loadlayout != 2)
+ {
+ dstream<<"Sector converted to new layout - deleting "<<
+ sectordir1<<std::endl;
+ fs::RecursiveDelete(sectordir1);
+ }
+
return true;
}
+
void ServerMap::saveBlock(MapBlock *block)
{
DSTACK(__FUNCTION_NAME);
@@ -5386,12 +5436,9 @@ void ServerMap::saveBlock(MapBlock *block)
// Get destination
v3s16 p3d = block->getPos();
v2s16 p2d(p3d.X, p3d.Z);
- createDir(m_savedir);
- createDir(m_savedir+"/sectors");
std::string dir = getSectorDir(p2d);
- createDir(dir);
+ createDirs(dir);
- // Block file is map/sectors/xxxxxxxx/xxxx
char cc[5];
snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
std::string fullpath = dir + "/" + cc;
@@ -5427,12 +5474,11 @@ void ServerMap::saveBlock(MapBlock *block)
block->resetChangedFlag();
}
-void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
+void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
{
DSTACK(__FUNCTION_NAME);
- // Block file is map/sectors/xxxxxxxx/xxxx
- std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
+ std::string fullpath = sectordir+"/"+blockfile;
try{
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
@@ -5496,7 +5542,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
*/
// Save old format blocks in new format
- if(version < SER_FMT_VER_HIGHEST)
+ if(version < SER_FMT_VER_HIGHEST || save_after_load)
{
saveBlock(block);
}
diff --git a/src/map.h b/src/map.h
index fa52dc26b..09154547c 100644
--- a/src/map.h
+++ b/src/map.h
@@ -545,13 +545,9 @@ public:
Misc. helper functions for fiddling with directory and file
names when saving
*/
- void createDir(std::string path);
- void createSaveDir();
- // returns something like "xxxxxxxx"
- std::string getSectorSubDir(v2s16 pos);
+ void createDirs(std::string path);
// returns something like "map/sectors/xxxxxxxx"
- std::string getSectorDir(v2s16 pos);
- std::string createSectorDir(v2s16 pos);
+ std::string getSectorDir(v2s16 pos, int layout = 2);
// dirname: final directory name
v2s16 getSectorPos(std::string dirname);
v3s16 getBlockPos(std::string sectordir, std::string blockfile);
@@ -572,7 +568,7 @@ public:
// (no MapBlocks)
// DEPRECATED? Sectors have no metadata anymore.
void saveSectorMeta(ServerMapSector *sector);
- MapSector* loadSectorMeta(std::string dirname);
+ MapSector* loadSectorMeta(std::string dirname, bool save_after_load);
// Full load of a sector including all blocks.
// returns true on success, false on failure.
@@ -583,7 +579,7 @@ public:
void saveBlock(MapBlock *block);
// This will generate a sector with getSector if not found.
- void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector);
+ void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
// For debug printing
virtual void PrintInfo(std::ostream &out);
diff --git a/src/player.cpp b/src/player.cpp
index 3f92e899c..539244709 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -87,6 +87,7 @@ Player::Player():
m_position(0,0,0)
{
updateName("<not set>");
+ updatePassword("");
resetInventory();
}
@@ -145,6 +146,7 @@ void Player::serialize(std::ostream &os)
Settings args;
args.setS32("version", 1);
args.set("name", m_name);
+ args.set("password", m_password);
args.setFloat("pitch", m_pitch);
args.setFloat("yaw", m_yaw);
args.setV3F("position", m_position);
@@ -179,6 +181,10 @@ void Player::deSerialize(std::istream &is)
//args.getS32("version");
std::string name = args.get("name");
updateName(name.c_str());
+ std::string password = "";
+ if(args.exists("password"))
+ password = args.get("password");
+ updatePassword(password.c_str());
m_pitch = args.getFloat("pitch");
m_yaw = args.getFloat("yaw");
m_position = args.getV3F("position");
diff --git a/src/player.h b/src/player.h
index be93766fd..925252e49 100644
--- a/src/player.h
+++ b/src/player.h
@@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "collision.h"
#define PLAYERNAME_SIZE 20
+#define PASSWORD_SIZE 28 // Maximum password length. Allows for
+ // base64-encoded SHA-1.
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,"
@@ -121,6 +123,16 @@ public:
return m_name;
}
+ virtual void updatePassword(const char *password)
+ {
+ snprintf(m_password, PASSWORD_SIZE, "%s", password);
+ }
+
+ const char * getPassword()
+ {
+ return m_password;
+ }
+
virtual bool isLocal() const = 0;
virtual void updateLight(u8 light_at_pos) {};
@@ -157,6 +169,7 @@ public:
protected:
char m_name[PLAYERNAME_SIZE];
+ char m_password[PASSWORD_SIZE];
f32 m_pitch;
f32 m_yaw;
v3f m_speed;
diff --git a/src/server.cpp b/src/server.cpp
index d211186eb..b5cb48a36 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1734,8 +1734,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// [0] u16 TOSERVER_INIT
// [2] u8 SER_FMT_VER_HIGHEST
// [3] u8[20] player_name
+ // [23] u8[28] password <--- can be sent without this, from old versions
- if(datasize < 3)
+ if(datasize < 2+1+PLAYERNAME_SIZE)
return;
derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
@@ -1767,17 +1768,41 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
*/
// Get player name
- const u32 playername_size = 20;
- char playername[playername_size];
- for(u32 i=0; i<playername_size-1; i++)
+ char playername[PLAYERNAME_SIZE];
+ for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
{
playername[i] = data[3+i];
}
- playername[playername_size-1] = 0;
-
+ playername[PLAYERNAME_SIZE-1] = 0;
+
+ // Get password
+ char password[PASSWORD_SIZE];
+ if(datasize == 2+1+PLAYERNAME_SIZE)
+ {
+ // old version - assume blank password
+ password[0] = 0;
+ }
+ else
+ {
+ for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+ {
+ password[i] = data[23+i];
+ }
+ password[PASSWORD_SIZE-1] = 0;
+ }
+ Player *checkplayer = m_env.getPlayer(playername);
+ if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
+ {
+ derr_server<<DTIME<<"Server: peer_id="<<peer_id
+ <<": supplied invalid password for "
+ <<playername<<std::endl;
+ SendAccessDenied(m_con, peer_id);
+ return;
+ }
+
// Get player
- Player *player = emergePlayer(playername, "", peer_id);
- //Player *player = m_env.getPlayer(peer_id);
+ Player *player = emergePlayer(playername, password, peer_id);
+
/*{
// DEBUG: Test serialization
@@ -3138,6 +3163,20 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
con.Send(peer_id, 0, data, true);
}
+void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_ACCESS_DENIED);
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ con.Send(peer_id, 0, data, true);
+}
+
/*
Non-static send methods
*/
@@ -4052,8 +4091,7 @@ v3f findSpawnPos(ServerMap &map)
), BS);
}
-Player *Server::emergePlayer(const char *name, const char *password,
- u16 peer_id)
+Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
{
/*
Try to get an existing player
@@ -4099,6 +4137,7 @@ Player *Server::emergePlayer(const char *name, const char *password,
//player->peer_id = PEER_ID_INEXISTENT;
player->peer_id = peer_id;
player->updateName(name);
+ player->updatePassword(password);
/*
Set player position
diff --git a/src/server.h b/src/server.h
index d8b47aef9..289f09618 100644
--- a/src/server.h
+++ b/src/server.h
@@ -436,6 +436,7 @@ private:
*/
static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
+ static void SendAccessDenied(con::Connection &con, u16 peer_id);
/*
Non-static send methods
@@ -476,11 +477,12 @@ private:
/*
Get a player from memory or creates one.
If player is already connected, return NULL
+ The password is not checked here - it is only used to
+ set the password if a new player is created.
Call with env and con locked.
*/
- Player *emergePlayer(const char *name, const char *password,
- u16 peer_id);
+ Player *emergePlayer(const char *name, const char *password, u16 peer_id);
/*
Update water pressure.
diff --git a/src/sha1.cpp b/src/sha1.cpp
new file mode 100644
index 000000000..98180adc7
--- /dev/null
+++ b/src/sha1.cpp
@@ -0,0 +1,207 @@
+/* sha1.cpp
+
+Copyright (c) 2005 Michael D. Leonhard
+
+http://tamale.net/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "sha1.h"
+
+// print out memory in hexadecimal
+void SHA1::hexPrinter( unsigned char* c, int l )
+{
+ assert( c );
+ assert( l > 0 );
+ while( l > 0 )
+ {
+ printf( " %02x", *c );
+ l--;
+ c++;
+ }
+}
+
+// circular left bit rotation. MSB wraps around to LSB
+Uint32 SHA1::lrot( Uint32 x, int bits )
+{
+ return (x<<bits) | (x>>(32 - bits));
+};
+
+// Save a 32-bit unsigned integer to memory, in big-endian order
+void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num )
+{
+ assert( byte );
+ byte[0] = (unsigned char)(num>>24);
+ byte[1] = (unsigned char)(num>>16);
+ byte[2] = (unsigned char)(num>>8);
+ byte[3] = (unsigned char)num;
+}
+
+
+// Constructor *******************************************************
+SHA1::SHA1()
+{
+ // make sure that the data type is the right size
+ assert( sizeof( Uint32 ) * 5 == 20 );
+
+ // initialize
+ H0 = 0x67452301;
+ H1 = 0xefcdab89;
+ H2 = 0x98badcfe;
+ H3 = 0x10325476;
+ H4 = 0xc3d2e1f0;
+ unprocessedBytes = 0;
+ size = 0;
+}
+
+// Destructor ********************************************************
+SHA1::~SHA1()
+{
+ // erase data
+ H0 = H1 = H2 = H3 = H4 = 0;
+ for( int c = 0; c < 64; c++ ) bytes[c] = 0;
+ unprocessedBytes = size = 0;
+}
+
+// process ***********************************************************
+void SHA1::process()
+{
+ assert( unprocessedBytes == 64 );
+ //printf( "process: " ); hexPrinter( bytes, 64 ); printf( "\n" );
+ int t;
+ Uint32 a, b, c, d, e, K, f, W[80];
+ // starting values
+ a = H0;
+ b = H1;
+ c = H2;
+ d = H3;
+ e = H4;
+ // copy and expand the message block
+ for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24)
+ +(bytes[t*4 + 1] << 16)
+ +(bytes[t*4 + 2] << 8)
+ + bytes[t*4 + 3];
+ for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 );
+
+ /* main loop */
+ Uint32 temp;
+ for( t = 0; t < 80; t++ )
+ {
+ if( t < 20 ) {
+ K = 0x5a827999;
+ f = (b & c) | ((b ^ 0xFFFFFFFF) & d);//TODO: try using ~
+ } else if( t < 40 ) {
+ K = 0x6ed9eba1;
+ f = b ^ c ^ d;
+ } else if( t < 60 ) {
+ K = 0x8f1bbcdc;
+ f = (b & c) | (b & d) | (c & d);
+ } else {
+ K = 0xca62c1d6;
+ f = b ^ c ^ d;
+ }
+ temp = lrot(a,5) + f + e + W[t] + K;
+ e = d;
+ d = c;
+ c = lrot(b,30);
+ b = a;
+ a = temp;
+ //printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e );
+ }
+ /* add variables */
+ H0 += a;
+ H1 += b;
+ H2 += c;
+ H3 += d;
+ H4 += e;
+ //printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 );
+ /* all bytes have been processed */
+ unprocessedBytes = 0;
+}
+
+// addBytes **********************************************************
+void SHA1::addBytes( const char* data, int num )
+{
+ assert( data );
+ assert( num > 0 );
+ // add these bytes to the running total
+ size += num;
+ // repeat until all data is processed
+ while( num > 0 )
+ {
+ // number of bytes required to complete block
+ int needed = 64 - unprocessedBytes;
+ assert( needed > 0 );
+ // number of bytes to copy (use smaller of two)
+ int toCopy = (num < needed) ? num : needed;
+ // Copy the bytes
+ memcpy( bytes + unprocessedBytes, data, toCopy );
+ // Bytes have been copied
+ num -= toCopy;
+ data += toCopy;
+ unprocessedBytes += toCopy;
+
+ // there is a full block
+ if( unprocessedBytes == 64 ) process();
+ }
+}
+
+// digest ************************************************************
+unsigned char* SHA1::getDigest()
+{
+ // save the message size
+ Uint32 totalBitsL = size << 3;
+ Uint32 totalBitsH = size >> 29;
+ // add 0x80 to the message
+ addBytes( "\x80", 1 );
+
+ unsigned char footer[64] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ // block has no room for 8-byte filesize, so finish it
+ if( unprocessedBytes > 56 )
+ addBytes( (char*)footer, 64 - unprocessedBytes);
+ assert( unprocessedBytes <= 56 );
+ // how many zeros do we need
+ int neededZeros = 56 - unprocessedBytes;
+ // store file size (in bits) in big-endian format
+ storeBigEndianUint32( footer + neededZeros , totalBitsH );
+ storeBigEndianUint32( footer + neededZeros + 4, totalBitsL );
+ // finish the final block
+ addBytes( (char*)footer, neededZeros + 8 );
+ // allocate memory for the digest bytes
+ unsigned char* digest = (unsigned char*)malloc( 20 );
+ // copy the digest bytes
+ storeBigEndianUint32( digest, H0 );
+ storeBigEndianUint32( digest + 4, H1 );
+ storeBigEndianUint32( digest + 8, H2 );
+ storeBigEndianUint32( digest + 12, H3 );
+ storeBigEndianUint32( digest + 16, H4 );
+ // return the digest
+ return digest;
+}
diff --git a/src/sha1.h b/src/sha1.h
new file mode 100644
index 000000000..c04947373
--- /dev/null
+++ b/src/sha1.h
@@ -0,0 +1,51 @@
+/* sha1.h
+
+Copyright (c) 2005 Michael D. Leonhard
+
+http://tamale.net/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+*/
+
+#ifndef SHA1_HEADER
+typedef unsigned int Uint32;
+
+class SHA1
+{
+ private:
+ // fields
+ Uint32 H0, H1, H2, H3, H4;
+ unsigned char bytes[64];
+ int unprocessedBytes;
+ Uint32 size;
+ void process();
+ public:
+ SHA1();
+ ~SHA1();
+ void addBytes( const char* data, int num );
+ unsigned char* getDigest();
+ // utility methods
+ static Uint32 lrot( Uint32 x, int bits );
+ static void storeBigEndianUint32( unsigned char* byte, Uint32 num );
+ static void hexPrinter( unsigned char* c, int l );
+};
+
+#define SHA1_HEADER
+#endif