From a8a3271470299820c5322523dc439415bc1ff8a0 Mon Sep 17 00:00:00 2001
From: Ciaran Gultnieks <ciaran@ciarang.com>
Date: Sun, 22 May 2011 21:09:12 +0100
Subject: Added the ability to change your password (via pause menu)

--HG--
extra : rebase_source : e8ec407f60711d42d33be4811b2880088f617b5b
---
 src/CMakeLists.txt        |   5 +-
 src/client.cpp            |  37 +++++++
 src/client.h              |   2 +
 src/clientserver.h        |  10 ++
 src/game.cpp              |   8 ++
 src/guiPasswordChange.cpp | 252 ++++++++++++++++++++++++++++++++++++++++++++++
 src/guiPasswordChange.h   |  57 +++++++++++
 src/guiPauseMenu.cpp      |  36 +++++--
 src/guiPauseMenu.h        |   1 +
 src/main.cpp              |  21 +---
 src/main.h                |   7 ++
 src/server.cpp            |  25 +++++
 src/utility.cpp           |  23 +++++
 src/utility.h             |   1 +
 14 files changed, 457 insertions(+), 28 deletions(-)
 create mode 100644 src/guiPasswordChange.cpp
 create mode 100644 src/guiPasswordChange.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 58596a101..49982d310 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -77,6 +77,8 @@ set(common_SRCS
 	player.cpp
 	utility.cpp
 	test.cpp
+	sha1.cpp
+	base64.cpp
 )
 
 # Client sources
@@ -91,12 +93,11 @@ set(minetest_SRCS
 	guiTextInputMenu.cpp
 	guiInventoryMenu.cpp
 	guiPauseMenu.cpp
+	guiPasswordChange.cpp
 	client.cpp
 	tile.cpp
 	game.cpp
 	main.cpp
-	sha1.cpp
-	base64.cpp
 )
 
 # Server sources
diff --git a/src/client.cpp b/src/client.cpp
index c85d6e9e2..5869dc77b 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -1602,6 +1602,43 @@ void Client::sendChatMessage(const std::wstring &message)
 	Send(0, data, true);
 }
 
+void Client::sendChangePassword(const std::wstring oldpassword,
+		const std::wstring newpassword)
+{
+	Player *player = m_env.getLocalPlayer();
+	if(player == NULL)
+		return;
+
+	std::string playername = player->getName();
+	std::string oldpwd = translatePassword(playername, oldpassword);
+	std::string newpwd = translatePassword(playername, newpassword);
+
+	std::ostringstream os(std::ios_base::binary);
+	u8 buf[2+PASSWORD_SIZE*2];
+	/*
+		[0] u16 TOSERVER_PASSWORD
+		[2] u8[28] old password
+		[30] u8[28] new password
+	*/
+
+	writeU16(buf, TOSERVER_PASSWORD);
+	for(u32 i=0;i<PASSWORD_SIZE-1;i++)
+	{
+		buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
+		buf[30+i] = i<newpwd.length()?newpwd[i]:0;
+	}
+	buf[2+PASSWORD_SIZE-1] = 0;
+	buf[30+PASSWORD_SIZE-1] = 0;
+	os.write((char*)buf, 2+PASSWORD_SIZE*2);
+
+	// Make data buffer
+	std::string s = os.str();
+	SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+	// Send as reliable
+	Send(0, data, true);
+}
+
+
 void Client::sendDamage(u8 damage)
 {
 	DSTACK(__FUNCTION_NAME);
diff --git a/src/client.h b/src/client.h
index 222f83ab7..f661838ce 100644
--- a/src/client.h
+++ b/src/client.h
@@ -255,6 +255,8 @@ public:
 	void sendSignNodeText(v3s16 p, std::string text);
 	void sendInventoryAction(InventoryAction *a);
 	void sendChatMessage(const std::wstring &message);
+	void sendChangePassword(const std::wstring oldpassword,
+		const std::wstring newpassword);
 	void sendDamage(u8 damage);
 	
 	// locks envlock
diff --git a/src/clientserver.h b/src/clientserver.h
index a64a11f08..256aed362 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -285,6 +285,16 @@ enum ToServerCommand
 		u16 command
 		u8 amount
 	*/
+
+	TOSERVER_PASSWORD=0x36,
+	/*
+		Sent to change password.
+
+		[0] u16 TOSERVER_PASSWORD
+		[2] u8[28] old password
+		[30] u8[28] new password
+	*/
+
 };
 
 inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)
diff --git a/src/game.cpp b/src/game.cpp
index 99e08b7bf..e82e4cd8e 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "client.h"
 #include "server.h"
 #include "guiPauseMenu.h"
+#include "guiPasswordChange.h"
 #include "guiInventoryMenu.h"
 #include "guiTextInputMenu.h"
 #include "guiFurnaceMenu.h"
@@ -912,6 +913,13 @@ void the_game(
 			break;
 		}
 
+		if(g_gamecallback->changepassword_requested)
+		{
+			(new GUIPasswordChange(guienv, guiroot, -1,
+				&g_menumgr, &client))->drop();
+			g_gamecallback->changepassword_requested = false;
+		}
+
 		/*
 			Process TextureSource's queue
 		*/
diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp
new file mode 100644
index 000000000..98b11b432
--- /dev/null
+++ b/src/guiPasswordChange.cpp
@@ -0,0 +1,252 @@
+/*
+Minetest-c55
+Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiPasswordChange.h"
+#include "debug.h"
+#include "serialization.h"
+#include <string>
+
+const int ID_oldPassword = 256;
+const int ID_newPassword1 = 257;
+const int ID_newPassword2 = 258;
+const int ID_change = 259;
+const int ID_message = 260;
+
+GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env,
+		gui::IGUIElement* parent, s32 id,
+		IMenuManager *menumgr,
+		Client* client
+):
+	GUIModalMenu(env, parent, id, menumgr),
+	m_client(client)
+{
+}
+
+GUIPasswordChange::~GUIPasswordChange()
+{
+	removeChildren();
+}
+
+void GUIPasswordChange::removeChildren()
+{
+	{
+		gui::IGUIElement *e = getElementFromId(ID_oldPassword);
+		if(e != NULL)
+			e->remove();
+	}
+	{
+		gui::IGUIElement *e = getElementFromId(ID_newPassword1);
+		if(e != NULL)
+			e->remove();
+	}
+	{
+		gui::IGUIElement *e = getElementFromId(ID_newPassword2);
+		if(e != NULL)
+			e->remove();
+	}
+	{
+		gui::IGUIElement *e = getElementFromId(ID_change);
+		if(e != NULL)
+			e->remove();
+	}
+}
+
+void GUIPasswordChange::regenerateGui(v2u32 screensize)
+{
+	/*
+		Remove stuff
+	*/
+	removeChildren();
+	
+	/*
+		Calculate new sizes and positions
+	*/
+	core::rect<s32> rect(
+			screensize.X/2 - 580/2,
+			screensize.Y/2 - 300/2,
+			screensize.X/2 + 580/2,
+			screensize.Y/2 + 300/2
+	);
+	
+	DesiredRect = rect;
+	recalculateAbsolutePosition(false);
+
+	v2s32 size = rect.getSize();
+	v2s32 topleft_client(40, 0);
+	v2s32 size_client = size - v2s32(40, 0);
+
+	/*
+		Add stuff
+	*/
+	s32 ypos = 50;
+	{
+		core::rect<s32> rect(0, 0, 110, 20);
+		rect += topleft_client + v2s32(35, ypos+6);
+		const wchar_t *text = L"Old Password";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+	}
+	{
+		core::rect<s32> rect(0, 0, 230, 30);
+		rect += topleft_client + v2s32(160, ypos);
+		gui::IGUIEditBox *e = 
+		Environment->addEditBox(L"", rect, true, this, ID_oldPassword);
+		Environment->setFocus(e);
+		e->setPasswordBox(true);
+	}
+	ypos += 50;
+	{
+		core::rect<s32> rect(0, 0, 110, 20);
+		rect += topleft_client + v2s32(35, ypos+6);
+		const wchar_t *text = L"New Password";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+	}
+	{
+		core::rect<s32> rect(0, 0, 230, 30);
+		rect += topleft_client + v2s32(160, ypos);
+		gui::IGUIEditBox *e = 
+		Environment->addEditBox(L"", rect, true, this, ID_newPassword1);
+		e->setPasswordBox(true);
+	}
+	ypos += 50;
+	{
+		core::rect<s32> rect(0, 0, 110, 20);
+		rect += topleft_client + v2s32(35, ypos+6);
+		const wchar_t *text = L"Confirm Password";
+		Environment->addStaticText(text, rect, false, true, this, -1);
+	}
+	{
+		core::rect<s32> rect(0, 0, 230, 30);
+		rect += topleft_client + v2s32(160, ypos);
+		gui::IGUIEditBox *e = 
+		Environment->addEditBox(L"", rect, true, this, ID_newPassword2);
+		e->setPasswordBox(true);
+	}
+
+	ypos += 50;
+	{
+		core::rect<s32> rect(0, 0, 140, 30);
+		rect = rect + v2s32(size.X/2-140/2, ypos);
+		Environment->addButton(rect, this, ID_change, L"Change");
+	}
+
+	ypos += 50;
+	{
+		core::rect<s32> rect(0, 0, 300, 20);
+		rect += topleft_client + v2s32(35, ypos);
+		const wchar_t *text = L"Passwords do not match!";
+		IGUIElement *e = 
+		Environment->addStaticText(text, rect, false, true, this, ID_message);
+		e->setVisible(false);
+	}
+
+}
+
+void GUIPasswordChange::drawMenu()
+{
+	gui::IGUISkin* skin = Environment->getSkin();
+	if (!skin)
+		return;
+	video::IVideoDriver* driver = Environment->getVideoDriver();
+	
+	video::SColor bgcolor(140,0,0,0);
+	driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
+
+	gui::IGUIElement::draw();
+}
+
+bool GUIPasswordChange::acceptInput()
+{
+		std::wstring oldpass;
+		std::wstring newpass;
+		gui::IGUIElement *e;
+		e = getElementFromId(ID_oldPassword);
+		if(e != NULL)
+			oldpass = e->getText();
+		e = getElementFromId(ID_newPassword1);
+		if(e != NULL)
+			newpass = e->getText();
+		e = getElementFromId(ID_newPassword2);
+		if(e != NULL && newpass != e->getText())
+		{
+			e = getElementFromId(ID_message);
+			if(e != NULL)
+				e->setVisible(true);
+			return false;
+		}
+		m_client->sendChangePassword(oldpass, newpass);
+		return true;
+}
+
+bool GUIPasswordChange::OnEvent(const SEvent& event)
+{
+	if(event.EventType==EET_KEY_INPUT_EVENT)
+	{
+		if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
+		{
+			quitMenu();
+			return true;
+		}
+		if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
+		{
+			if(acceptInput())
+				quitMenu();
+			return true;
+		}
+	}
+	if(event.EventType==EET_GUI_EVENT)
+	{
+		if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
+				&& isVisible())
+		{
+			if(!canTakeFocus(event.GUIEvent.Element))
+			{
+				dstream<<"GUIPasswordChange: Not allowing focus change."
+						<<std::endl;
+				// Returning true disables focus change
+				return true;
+			}
+		}
+		if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
+		{
+			switch(event.GUIEvent.Caller->getID())
+			{
+			case ID_change:
+				if(acceptInput())
+					quitMenu();
+				return true;
+			}
+		}
+		if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
+		{
+			switch(event.GUIEvent.Caller->getID())
+			{
+			case ID_oldPassword:
+			case ID_newPassword1:
+			case ID_newPassword2:
+				if(acceptInput())
+					quitMenu();
+				return true;
+			}
+		}
+	}
+
+	return Parent ? Parent->OnEvent(event) : false;
+}
+
diff --git a/src/guiPasswordChange.h b/src/guiPasswordChange.h
new file mode 100644
index 000000000..defac3113
--- /dev/null
+++ b/src/guiPasswordChange.h
@@ -0,0 +1,57 @@
+/*
+Minetest-c55
+Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef GUIPASSWORDCHANGE_HEADER
+#define GUIPASSWORDCHANGE_HEADER
+
+#include "common_irrlicht.h"
+#include "modalMenu.h"
+#include "utility.h"
+#include "client.h"
+#include <string>
+
+class GUIPasswordChange : public GUIModalMenu
+{
+public:
+	GUIPasswordChange(gui::IGUIEnvironment* env,
+			gui::IGUIElement* parent, s32 id,
+			IMenuManager *menumgr,
+			Client* client);
+	~GUIPasswordChange();
+	
+	void removeChildren();
+	/*
+		Remove and re-add (or reposition) stuff
+	*/
+	void regenerateGui(v2u32 screensize);
+
+	void drawMenu();
+
+	bool acceptInput();
+
+	bool OnEvent(const SEvent& event);
+	
+private:
+	Client* m_client;
+
+};
+
+#endif
+
diff --git a/src/guiPauseMenu.cpp b/src/guiPauseMenu.cpp
index 2d42fdb77..d32d1a10b 100644
--- a/src/guiPauseMenu.cpp
+++ b/src/guiPauseMenu.cpp
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h"
 #include "porting.h"
 #include "config.h"
+#include "main.h"
 
 GUIPauseMenu::GUIPauseMenu(gui::IGUIEnvironment* env,
 		gui::IGUIElement* parent, s32 id,
@@ -64,6 +65,11 @@ void GUIPauseMenu::removeChildren()
 		if(e != NULL)
 			e->remove();
 	}
+	{
+		gui::IGUIElement *e = getElementFromId(261);
+		if(e != NULL)
+			e->remove();
+	}
 }
 
 void GUIPauseMenu::regenerateGui(v2u32 screensize)
@@ -91,21 +97,34 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
 	/*
 		Add stuff
 	*/
+	const s32 btn_height = 30;
+	const s32 btn_gap = 20;
+	const s32 btn_num = 4;
+	s32 btn_y = size.Y/2-((btn_num*btn_height+(btn_num-1)*btn_gap))/2;
 	{
-		core::rect<s32> rect(0, 0, 140, 30);
-		rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2-50);
+		core::rect<s32> rect(0, 0, 140, btn_height);
+		rect = rect + v2s32(size.X/2-140/2, btn_y);
 		Environment->addButton(rect, this, 256, L"Continue");
 	}
+	btn_y += btn_height + btn_gap;
+	{
+		core::rect<s32> rect(0, 0, 140, btn_height);
+		rect = rect + v2s32(size.X/2-140/2, btn_y);
+		Environment->addButton(rect, this, 261, L"Change Password");
+	}
+	btn_y += btn_height + btn_gap;
 	{
-		core::rect<s32> rect(0, 0, 140, 30);
-		rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+0);
+		core::rect<s32> rect(0, 0, 140, btn_height);
+		rect = rect + v2s32(size.X/2-140/2, btn_y);
 		Environment->addButton(rect, this, 260, L"Disconnect");
 	}
+	btn_y += btn_height + btn_gap;
 	{
-		core::rect<s32> rect(0, 0, 140, 30);
-		rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+50);
+		core::rect<s32> rect(0, 0, 140, btn_height);
+		rect = rect + v2s32(size.X/2-140/2, btn_y);
 		Environment->addButton(rect, this, 257, L"Exit to OS");
 	}
+
 	{
 		core::rect<s32> rect(0, 0, 180, 240);
 		rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
@@ -172,6 +191,7 @@ void GUIPauseMenu::drawMenu()
 
 bool GUIPauseMenu::OnEvent(const SEvent& event)
 {
+
 	if(event.EventType==EET_KEY_INPUT_EVENT)
 	{
 		if(event.KeyInput.PressedDown)
@@ -209,6 +229,10 @@ bool GUIPauseMenu::OnEvent(const SEvent& event)
 				quitMenu();
 				// ALWAYS return immediately after quitMenu()
 				return true;
+			case 261:
+				quitMenu();
+				m_gamecallback->changePassword();
+				return true;
 			case 260: // disconnect
 				m_gamecallback->disconnect();
 				quitMenu();
diff --git a/src/guiPauseMenu.h b/src/guiPauseMenu.h
index 22cb65b2c..64e3c71f1 100644
--- a/src/guiPauseMenu.h
+++ b/src/guiPauseMenu.h
@@ -28,6 +28,7 @@ class IGameCallback
 public:
 	virtual void exitToOS() = 0;
 	virtual void disconnect() = 0;
+	virtual void changePassword() = 0;
 };
 
 class GUIPauseMenu : public GUIModalMenu
diff --git a/src/main.cpp b/src/main.cpp
index c3b075732..2913d019d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -353,8 +353,6 @@ 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;
@@ -1468,24 +1466,7 @@ 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 = "";
-				}
+				password = translatePassword(playername, menudata.password);
 
 				address = wide_to_narrow(menudata.address);
 				int newport = stoi(wide_to_narrow(menudata.port));
diff --git a/src/main.h b/src/main.h
index c8ac7ccf1..fcf150f87 100644
--- a/src/main.h
+++ b/src/main.h
@@ -119,6 +119,7 @@ class MainGameCallback : public IGameCallback
 public:
 	MainGameCallback(IrrlichtDevice *a_device):
 		disconnect_requested(false),
+		changepassword_requested(false),
 		device(a_device)
 	{
 	}
@@ -133,7 +134,13 @@ public:
 		disconnect_requested = true;
 	}
 
+	virtual void changePassword()
+	{
+		changepassword_requested = true;
+	}
+
 	bool disconnect_requested;
+	bool changepassword_requested;
 	IrrlichtDevice *device;
 };
 
diff --git a/src/server.cpp b/src/server.cpp
index 84f9a28e0..5c03ea8f6 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -3004,6 +3004,31 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
 		SendPlayerHP(player);
 	}
+	else if(command == TOSERVER_PASSWORD)
+	{
+		/*
+			[0] u16 TOSERVER_PASSWORD
+			[2] u8[28] old password
+			[30] u8[28] new password
+		*/
+
+		if(datasize != 2+PASSWORD_SIZE*2)
+			return;
+		char password[PASSWORD_SIZE];
+		for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+			password[i] = data[2+i];
+		password[PASSWORD_SIZE-1] = 0;
+		if(strcmp(player->getPassword(),password))
+		{
+			// Wrong old password supplied!!
+			SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
+			return;
+		}
+		for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+			password[i] = data[30+i];
+		player->updatePassword(password);
+		SendChatMessage(peer_id, L"Password change successful");
+	}
 	else
 	{
 		derr_server<<"WARNING: Server::ProcessData(): Ignoring "
diff --git a/src/utility.cpp b/src/utility.cpp
index fc657b27b..186881c5a 100644
--- a/src/utility.cpp
+++ b/src/utility.cpp
@@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "utility.h"
 #include "gettime.h"
+#include "sha1.h"
+#include "base64.h"
 
 TimeTaker::TimeTaker(const char *name, u32 *result)
 {
@@ -217,3 +219,24 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range,
 	return true;
 }
 
+// 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).
+std::string translatePassword(std::string playername, std::wstring password)
+{
+	if(password.length() == 0)
+		return "";
+
+	std::string slt=playername + wide_to_narrow(password);
+	SHA1 *sha1 = new SHA1();
+	sha1->addBytes(slt.c_str(), slt.length());
+	unsigned char *digest = sha1->getDigest();
+	std::string pwd = base64_encode(digest, 20);
+	free(digest);
+	return pwd;
+}
+
+
+
diff --git a/src/utility.h b/src/utility.h
index f32dc3acf..c7513e94d 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -2112,6 +2112,7 @@ protected:
 	float m_accumulator;
 };
 
+std::string translatePassword(std::string playername, std::wstring password);
 
 #endif
 
-- 
cgit v1.2.3