13 Commits
db ... master

Author SHA1 Message Date
Erik
0078be8e9a Update Windows Github Actions runner from 2019 to 2022 (#309)
* Attempt to update Windows action runner to 2022

* Fix VS path
2025-07-24 01:27:27 -05:00
b617456aa1 Include tabledata in CI builds 2025-02-11 17:30:35 -08:00
935ee1bf6f CI fixes 2025-02-11 17:19:27 -08:00
43a2504357 Update Dockerfile, license, and readme 2025-02-10 18:43:01 -08:00
ca196bf620 Add windows dev setup script 2025-01-30 21:33:05 -08:00
6b9ae4c325 Validate name wheel names 2025-01-06 23:43:37 -05:00
d06c324aa3 Send namereq event on name change as well 2025-01-05 22:30:55 -05:00
052196d1cd Treat bcast areas 1 and 2 as global 2025-01-02 18:26:00 -05:00
e84f6505b8 Name request monitor events 2025-01-02 18:06:06 -05:00
b483bf7190 Respect name check flag in getPlayerName 2025-01-02 17:44:37 -05:00
6ff51685a8 Move event buffers to Monitor 2025-01-02 17:39:16 -05:00
b4ed31d4fb Dedicated bcast monitor event 2025-01-02 17:18:53 -05:00
36e0667ed2 Add Email and LastPasswordReset columns to Account table (#299)
* Add Email column to Account table

* Add LastPasswordReset timestamp column, missing DB version bump
2024-11-28 23:01:29 -05:00
22 changed files with 305 additions and 86 deletions

View File

@@ -8,6 +8,7 @@ on:
- .github/workflows/check-builds.yaml - .github/workflows/check-builds.yaml
- CMakeLists.txt - CMakeLists.txt
- Makefile - Makefile
- tdata
pull_request: pull_request:
types: [opened, reopened, synchronize, ready_for_review] types: [opened, reopened, synchronize, ready_for_review]
paths: paths:
@@ -19,7 +20,7 @@ on:
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
steps: steps:
- name: Set environment - name: Set environment
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
@@ -48,6 +49,7 @@ jobs:
Rename-Item -Path "bin/fusion" -newName "$version-fusion" Rename-Item -Path "bin/fusion" -newName "$version-fusion"
Write-Output "Built version $version" Write-Output "Built version $version"
} }
Copy-Item -Path "tdata" -Destination "bin/tdata" -Recurse
Copy-Item -Path "sql" -Destination "bin/sql" -Recurse Copy-Item -Path "sql" -Destination "bin/sql" -Recurse
Copy-Item -Path "config.ini" -Destination "bin" Copy-Item -Path "config.ini" -Destination "bin"
shell: pwsh shell: pwsh
@@ -58,7 +60,7 @@ jobs:
path: bin path: bin
windows-build: windows-build:
runs-on: windows-2019 runs-on: windows-2022
steps: steps:
- name: Set environment - name: Set environment
run: $s = $env:GITHUB_SHA.subString(0, 7); echo "SHORT_SHA=$s" >> $env:GITHUB_ENV run: $s = $env:GITHUB_SHA.subString(0, 7); echo "SHORT_SHA=$s" >> $env:GITHUB_ENV
@@ -73,7 +75,7 @@ jobs:
$configurations = "Release" $configurations = "Release"
# "Debug" builds are disabled, since we don't really need them # "Debug" builds are disabled, since we don't really need them
$vsPath = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise" $vsPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
Import-Module "$vsPath\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" Import-Module "$vsPath\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation
@@ -100,6 +102,7 @@ jobs:
} }
Rename-Item -Path "bin/$configuration" -newName "$version-$configuration" Rename-Item -Path "bin/$configuration" -newName "$version-$configuration"
Write-Output "Built version $version $configuration" Write-Output "Built version $version $configuration"
Copy-Item -Path "tdata" -Destination "bin/$version-$configuration/tdata" -Recurse
Copy-Item -Path "sql" -Destination "bin/$version-$configuration/sql" -Recurse Copy-Item -Path "sql" -Destination "bin/$version-$configuration/sql" -Recurse
Copy-Item -Path "config.ini" -Destination "bin/$version-$configuration" Copy-Item -Path "config.ini" -Destination "bin/$version-$configuration"
} }
@@ -108,11 +111,11 @@ jobs:
- name: Upload build artifact - name: Upload build artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: 'windows-vs2019-bin-x64-${{ env.SHORT_SHA }}' name: 'windows-vs2022-bin-x64-${{ env.SHORT_SHA }}'
path: bin path: bin
copy-artifacts: copy-artifacts:
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' if: github.event_name != 'pull_request' && (github.ref_type == 'tag' || github.ref_name == 'master')
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [windows-build, ubuntu-build] needs: [windows-build, ubuntu-build]
env: env:
@@ -121,7 +124,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: recursive
fetch-depth: 0 fetch-depth: 0
- run: | - run: |
GITDESC=$(git describe --tags) GITDESC=$(git describe --tags)

View File

@@ -38,4 +38,4 @@ EXPOSE 23000/tcp
EXPOSE 23001/tcp EXPOSE 23001/tcp
EXPOSE 8003/tcp EXPOSE 8003/tcp
LABEL Name=openfusion Version=1.6.0 LABEL Name=openfusion Version=2.0.0

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020-2024 OpenFusion Contributors Copyright (c) 2020-2025 OpenFusion Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -14,22 +14,22 @@ OpenFusion is a reverse-engineered server for FusionFall. It primarily targets v
### Getting Started ### Getting Started
#### Method A: Installer (Easiest) #### Method A: Installer (Easiest)
1. Download the client installer by clicking [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.6/OpenFusionClient-1.6-Installer.exe) - choose to run the file. 1. Download the launcher installer by clicking [here](https://github.com/OpenFusionProject/OpenFusionLauncher/releases/latest/download/OpenFusionLauncher-Windows-Installer.exe) - choose to run the file.
2. After a few moments, the client should open: you will be given a choice between two public servers by default. Select the one you wish to play and click connect. 2. After a few moments, the launcher should open: you will be given a choice between two public servers by default. Select the one you wish to play and click connect.
3. To create an account, simply enter the details you wish to use at the login screen then click Log In. Do *not* click register, as this will just lead to a blank screen. 3. To create an account, simply enter the details you wish to use at the login screen then click Log In. Do *not* click register, as this will just lead to a blank screen.
4. Make a new character, and enjoy the game! Your progress will be saved automatically, and you can resume playing by entering the login details you used in step 3. 4. Make a new character, and enjoy the game! Your progress will be saved automatically, and you can resume playing by entering the login details you used in step 3.
#### Method B: Standalone .zip file #### Method B: Standalone .zip file
1. Download the client from [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.6/OpenFusionClient-1.6.zip). 1. Download the launcher from [here](https://github.com/OpenFusionProject/OpenFusionLauncher/releases/latest/download/OpenFusionLauncher-Windows-Portable.zip).
2. Extract it to a folder of your choice. Note: if you are upgrading from an older version, it is preferable to start with a fresh folder rather than overwriting a previous install. 2. Extract it to a folder of your choice. Note: if you are upgrading from an older version, it is preferable to start with a fresh folder rather than overwriting a previous install.
3. Run OpenFusionClient.exe - you will be given a choice between two public servers by default. Select the one you wish to play and click connect. 3. Run OpenFusionLauncher.exe - you will be given a choice between two public servers by default. Select the one you wish to play and click connect.
4. To create an account, simply enter the details you wish to use at the login screen then click Log In. Do *not* click register, as this will just lead to a blank screen. 4. To create an account, simply enter the details you wish to use at the login screen then click Log In. Do *not* click register, as this will just lead to a blank screen.
5. Make a new character, and enjoy the game! Your progress will be saved automatically, and you can resume playing by entering the login details you used in step 4. 5. Make a new character, and enjoy the game! Your progress will be saved automatically, and you can resume playing by entering the login details you used in step 4.
Instructions for getting the client to run on Linux through Wine can be found [here](https://openfusion.dev/docs/guides/running-on-linux/). Instructions for getting the client to run on Linux through Wine can be found [here](https://openfusion.dev/docs/guides/running-on-linux/).
### Hosting a server ### Hosting a server
1. Grab `OpenFusionServer-1.6-Original.zip` or `OpenFusionServer-1.6-Academy.zip` from [here](https://github.com/OpenFusionProject/OpenFusion/releases/tag/1.6). 1. Grab `OpenFusionServer-Windows-Original.zip` or `OpenFusionServer-Windows-Academy.zip` from [here](https://github.com/OpenFusionProject/OpenFusion/releases/latest).
2. Extract it to a folder of your choice, then run `winfusion.exe` (Windows) or `fusion` (Linux) to start the server. 2. Extract it to a folder of your choice, then run `winfusion.exe` (Windows) or `fusion` (Linux) to start the server.
3. Add a new server to the client's list: 3. Add a new server to the client's list:
1. For Description, enter anything you want. This is what will show up in the server list. 1. For Description, enter anything you want. This is what will show up in the server list.

View File

@@ -12,6 +12,8 @@ sandbox=true
[login] [login]
# must be kept in sync with loginInfo.php # must be kept in sync with loginInfo.php
port=23000 port=23000
# will all name wheel names be approved instantly?
acceptallwheelnames=true
# will all custom names be approved instantly? # will all custom names be approved instantly?
acceptallcustomnames=true acceptallcustomnames=true
# should attempts to log into non-existent accounts # should attempts to log into non-existent accounts

8
sql/migration5.sql Normal file
View File

@@ -0,0 +1,8 @@
BEGIN TRANSACTION;
-- New Columns
ALTER TABLE Accounts ADD Email TEXT DEFAULT '' NOT NULL;
ALTER TABLE Accounts ADD LastPasswordReset INTEGER DEFAULT 0 NOT NULL;
-- Update DB Version
UPDATE Meta SET Value = 6 WHERE Key = 'DatabaseVersion';
UPDATE Meta SET Value = strftime('%s', 'now') WHERE Key = 'LastMigration';
COMMIT;

View File

@@ -1,14 +1,16 @@
CREATE TABLE IF NOT EXISTS Accounts ( CREATE TABLE IF NOT EXISTS Accounts (
AccountID INTEGER NOT NULL, AccountID INTEGER NOT NULL,
Login TEXT NOT NULL UNIQUE COLLATE NOCASE, Login TEXT NOT NULL UNIQUE COLLATE NOCASE,
Password TEXT NOT NULL, Password TEXT NOT NULL,
Selected INTEGER DEFAULT 1 NOT NULL, Selected INTEGER DEFAULT 1 NOT NULL,
AccountLevel INTEGER NOT NULL, AccountLevel INTEGER NOT NULL,
Created INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL, Created INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
LastLogin INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL, LastLogin INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
BannedUntil INTEGER DEFAULT 0 NOT NULL, BannedUntil INTEGER DEFAULT 0 NOT NULL,
BannedSince INTEGER DEFAULT 0 NOT NULL, BannedSince INTEGER DEFAULT 0 NOT NULL,
BanReason TEXT DEFAULT '' NOT NULL, BanReason TEXT DEFAULT '' NOT NULL,
Email TEXT DEFAULT '' NOT NULL,
LastPasswordReset INTEGER DEFAULT 0 NOT NULL,
PRIMARY KEY(AccountID AUTOINCREMENT) PRIMARY KEY(AccountID AUTOINCREMENT)
); );

View File

@@ -1,6 +1,7 @@
#include "Chat.hpp" #include "Chat.hpp"
#include "servers/CNShardServer.hpp" #include "servers/CNShardServer.hpp"
#include "servers/Monitor.hpp"
#include "Player.hpp" #include "Player.hpp"
#include "PlayerManager.hpp" #include "PlayerManager.hpp"
@@ -8,8 +9,6 @@
#include <assert.h> #include <assert.h>
std::vector<std::string> Chat::dump;
using namespace Chat; using namespace Chat;
static void chatHandler(CNSocket* sock, CNPacketData* data) { static void chatHandler(CNSocket* sock, CNPacketData* data) {
@@ -28,7 +27,7 @@ static void chatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[FreeChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[FreeChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, resp);
@@ -51,7 +50,7 @@ static void menuChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[MenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[MenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, resp);
@@ -103,14 +102,14 @@ static void announcementHandler(CNSocket* sock, CNPacketData* data) {
memcpy(msg.szAnnounceMsg, announcement->szAnnounceMsg, sizeof(msg.szAnnounceMsg)); memcpy(msg.szAnnounceMsg, announcement->szAnnounceMsg, sizeof(msg.szAnnounceMsg));
std::map<CNSocket*, Player*>::iterator it; std::map<CNSocket*, Player*>::iterator it;
// This value is completely arbitrary, but these make the most sense when you consider the architecture of the game
switch (announcement->iAreaType) { switch (announcement->iAreaType) {
case 0: // area (all players in viewable chunks) case 0: // area (all players in viewable chunks)
sock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE); sock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
PlayerManager::sendToViewable(sock, msg, P_FE2CL_GM_REP_PC_ANNOUNCE); PlayerManager::sendToViewable(sock, msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
break; break;
case 1: // shard case 1: // channel
case 2: // world case 2: // shard
break; // not applicable to OpenFusion
case 3: // global (all players) case 3: // global (all players)
for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++) { for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++) {
CNSocket* allSock = it->first; CNSocket* allSock = it->first;
@@ -120,9 +119,12 @@ static void announcementHandler(CNSocket* sock, CNPacketData* data) {
break; break;
} }
std::string logLine = "[Bcast " + std::to_string(announcement->iAreaType) + "] " + PlayerManager::getPlayerName(plr, false) + ": " + AUTOU16TOU8(msg.szAnnounceMsg); std::string logLine = std::to_string(announcement->iAreaType) + " "
std::cout << logLine << std::endl; + std::to_string(announcement->iAnnounceType) + " "
dump.push_back("**" + logLine + "**"); + std::to_string(announcement->iDuringTime) + " "
+ PlayerManager::getPlayerName(plr, false) + ": " + AUTOU16TOU8(msg.szAnnounceMsg);
std::cout << "Broadcast " << logLine << std::endl;
Monitor::bcasts.push_back(logLine);
} }
// Buddy freechatting // Buddy freechatting
@@ -155,7 +157,7 @@ static void buddyChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[BuddyChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::string logLine = "[BuddyChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat));
@@ -185,7 +187,7 @@ static void buddyMenuChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[BuddyMenuChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::string logLine = "[BuddyMenuChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat));
@@ -218,7 +220,7 @@ static void tradeChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
resp.iEmoteCode = pacdat->iEmoteCode; resp.iEmoteCode = pacdat->iEmoteCode;
sock->sendPacket(resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT); sock->sendPacket(resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT);
@@ -241,7 +243,7 @@ static void groupChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[GroupChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[GroupChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp);
@@ -264,7 +266,7 @@ static void groupMenuChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[GroupMenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[GroupMenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp);

View File

@@ -8,7 +8,6 @@
#include <vector> #include <vector>
namespace Chat { namespace Chat {
extern std::vector<std::string> dump;
void init(); void init();
void sendServerMessage(CNSocket* sock, std::string msg); // uses MOTD void sendServerMessage(CNSocket* sock, std::string msg); // uses MOTD

View File

@@ -3,6 +3,7 @@
#include "core/Core.hpp" #include "core/Core.hpp"
#include "db/Database.hpp" #include "db/Database.hpp"
#include "servers/CNShardServer.hpp" #include "servers/CNShardServer.hpp"
#include "servers/Monitor.hpp"
#include "PlayerManager.hpp" #include "PlayerManager.hpp"
#include "Items.hpp" #include "Items.hpp"
@@ -10,8 +11,6 @@
using namespace Email; using namespace Email;
std::vector<std::string> Email::dump;
// New email notification // New email notification
static void emailUpdateCheck(CNSocket* sock, CNPacketData* data) { static void emailUpdateCheck(CNSocket* sock, CNPacketData* data) {
INITSTRUCT(sP_FE2CL_REP_PC_NEW_EMAIL, resp); INITSTRUCT(sP_FE2CL_REP_PC_NEW_EMAIL, resp);
@@ -324,7 +323,7 @@ static void emailSend(CNSocket* sock, CNPacketData* data) {
std::string logEmail = "[Email] " + PlayerManager::getPlayerName(plr, true) + " (to " + PlayerManager::getPlayerName(&otherPlr, true) + "): <" + email.SubjectLine + ">\n" + email.MsgBody; std::string logEmail = "[Email] " + PlayerManager::getPlayerName(plr, true) + " (to " + PlayerManager::getPlayerName(&otherPlr, true) + "): <" + email.SubjectLine + ">\n" + email.MsgBody;
std::cout << logEmail << std::endl; std::cout << logEmail << std::endl;
dump.push_back(logEmail); Monitor::emails.push_back(logEmail);
// notification to recipient if online // notification to recipient if online
CNSocket* recipient = PlayerManager::getSockFromID(pkt->iTo_PCUID); CNSocket* recipient = PlayerManager::getSockFromID(pkt->iTo_PCUID);

View File

@@ -4,7 +4,5 @@
#include <string> #include <string>
namespace Email { namespace Email {
extern std::vector<std::string> dump;
void init(); void init();
} }

View File

@@ -620,6 +620,10 @@ std::string PlayerManager::getPlayerName(Player *plr, bool id) {
if (plr == nullptr) if (plr == nullptr)
return "NOT IN GAME"; return "NOT IN GAME";
if (plr->PCStyle.iNameCheck != 1) {
return "Player " + std::to_string(plr->iID);
}
std::string ret = ""; std::string ret = "";
if (id && plr->accountLevel <= 30) if (id && plr->accountLevel <= 30)
ret += "(GM) "; ret += "(GM) ";

View File

@@ -1,5 +1,7 @@
#include "TableData.hpp" #include "TableData.hpp"
#include "servers/CNLoginServer.hpp"
#include "NPCManager.hpp" #include "NPCManager.hpp"
#include "Missions.hpp" #include "Missions.hpp"
#include "Items.hpp" #include "Items.hpp"
@@ -79,6 +81,25 @@ static void loadXDT(json& xdtData) {
NPCManager::NPCData = xdtData["m_pNpcTable"]["m_pNpcData"]; NPCManager::NPCData = xdtData["m_pNpcTable"]["m_pNpcData"];
try { try {
// load name wheel names
json firstNameData = xdtData["m_pNameTable"]["m_pFirstName"];
for (json::iterator _name = firstNameData.begin(); _name != firstNameData.end(); _name++) {
auto name = _name.value();
LoginServer::WheelFirstNames.push_back(name["m_pstrNameString"]);
}
json middleNameData = xdtData["m_pNameTable"]["m_pMiddleName"];
for (json::iterator _name = middleNameData.begin(); _name != middleNameData.end(); _name++) {
auto name = _name.value();
LoginServer::WheelMiddleNames.push_back(name["m_pstrNameString"]);
}
json lastNameData = xdtData["m_pNameTable"]["m_pLastName"];
for (json::iterator _name = lastNameData.begin(); _name != lastNameData.end(); _name++) {
auto name = _name.value();
LoginServer::WheelLastNames.push_back(name["m_pstrNameString"]);
}
// load warps // load warps
json warpData = xdtData["m_pInstanceTable"]["m_pWarpData"]; json warpData = xdtData["m_pInstanceTable"]["m_pWarpData"];

View File

@@ -5,7 +5,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#define DATABASE_VERSION 5 #define DATABASE_VERSION 6
namespace Database { namespace Database {
@@ -69,7 +69,7 @@ namespace Database {
bool isNameFree(std::string firstName, std::string lastName); bool isNameFree(std::string firstName, std::string lastName);
bool isSlotFree(int accountId, int slotNum); bool isSlotFree(int accountId, int slotNum);
/// returns ID, 0 if something failed /// returns ID, 0 if something failed
int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID); int createCharacter(int slot, int accountId, const char* firstName, const char* lastName, int nameCheck);
/// returns true if query succeeded /// returns true if query succeeded
bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character, int accountId); bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character, int accountId);
/// returns true if query succeeded /// returns true if query succeeded
@@ -85,7 +85,7 @@ namespace Database {
}; };
void evaluateCustomName(int characterID, CustomName decision); void evaluateCustomName(int characterID, CustomName decision);
/// returns true if query succeeded /// returns true if query succeeded
bool changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId); bool changeName(int playerId, int accountId, const char* firstName, const char* lastName, int nameCheck);
// getting players // getting players
void getPlayer(Player* plr, int id); void getPlayer(Player* plr, int id);

View File

@@ -2,6 +2,8 @@
#include "db/internal.hpp" #include "db/internal.hpp"
#include "servers/CNLoginServer.hpp"
#include "bcrypt/BCrypt.hpp" #include "bcrypt/BCrypt.hpp"
void Database::findAccount(Account* account, std::string login) { void Database::findAccount(Account* account, std::string login) {
@@ -291,7 +293,7 @@ bool Database::isSlotFree(int accountId, int slotNum) {
return result; return result;
} }
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) { int Database::createCharacter(int slot, int accountId, const char* firstName, const char* lastName, int nameCheck) {
std::lock_guard<std::mutex> lock(dbCrit); std::lock_guard<std::mutex> lock(dbCrit);
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
@@ -304,22 +306,17 @@ int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
)"; )";
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
std::string firstName = AUTOU16TOU8(save->szFirstName);
std::string lastName = AUTOU16TOU8(save->szLastName);
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, AccountID); sqlite3_bind_int(stmt, 1, accountId);
sqlite3_bind_int(stmt, 2, save->iSlotNum); sqlite3_bind_int(stmt, 2, slot);
sqlite3_bind_text(stmt, 3, firstName.c_str(), -1, NULL); sqlite3_bind_text(stmt, 3, firstName, -1, NULL);
sqlite3_bind_text(stmt, 4, lastName.c_str(), -1, NULL); sqlite3_bind_text(stmt, 4, lastName, -1, NULL);
sqlite3_bind_int(stmt, 5, settings::SPAWN_X); sqlite3_bind_int(stmt, 5, settings::SPAWN_X);
sqlite3_bind_int(stmt, 6, settings::SPAWN_Y); sqlite3_bind_int(stmt, 6, settings::SPAWN_Y);
sqlite3_bind_int(stmt, 7, settings::SPAWN_Z); sqlite3_bind_int(stmt, 7, settings::SPAWN_Z);
sqlite3_bind_int(stmt, 8, settings::SPAWN_ANGLE); sqlite3_bind_int(stmt, 8, settings::SPAWN_ANGLE);
sqlite3_bind_int(stmt, 9, PC_MAXHEALTH(1)); sqlite3_bind_int(stmt, 9, PC_MAXHEALTH(1));
// if FNCode isn't 0, it's a wheel name
int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0;
sqlite3_bind_int(stmt, 10, nameCheck); sqlite3_bind_int(stmt, 10, nameCheck);
// blobs // blobs
@@ -648,7 +645,7 @@ void Database::evaluateCustomName(int characterID, CustomName decision) {
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
} }
bool Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId) { bool Database::changeName(int playerId, int accountId, const char* firstName, const char* lastName, int nameCheck) {
std::lock_guard<std::mutex> lock(dbCrit); std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"( const char* sql = R"(
@@ -662,15 +659,10 @@ bool Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId) {
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
std::string firstName = AUTOU16TOU8(save->szFirstName); sqlite3_bind_text(stmt, 1, firstName, -1, NULL);
std::string lastName = AUTOU16TOU8(save->szLastName); sqlite3_bind_text(stmt, 2, lastName, -1, NULL);
sqlite3_bind_text(stmt, 1, firstName.c_str(), -1, NULL);
sqlite3_bind_text(stmt, 2, lastName.c_str(), -1, NULL);
// if FNCode isn't 0, it's a wheel name
int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0;
sqlite3_bind_int(stmt, 3, nameCheck); sqlite3_bind_int(stmt, 3, nameCheck);
sqlite3_bind_int(stmt, 4, save->iPCUID); sqlite3_bind_int(stmt, 4, playerId);
sqlite3_bind_int(stmt, 5, accountId); sqlite3_bind_int(stmt, 5, accountId);
int rc = sqlite3_step(stmt); int rc = sqlite3_step(stmt);

View File

@@ -1,4 +1,5 @@
#include "servers/CNLoginServer.hpp" #include "servers/CNLoginServer.hpp"
#include "servers/Monitor.hpp"
#include "core/CNShared.hpp" #include "core/CNShared.hpp"
#include "db/Database.hpp" #include "db/Database.hpp"
@@ -12,6 +13,12 @@
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions; std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
namespace LoginServer {
std::vector<std::string> WheelFirstNames;
std::vector<std::string> WheelMiddleNames;
std::vector<std::string> WheelLastNames;
}
CNLoginServer::CNLoginServer(uint16_t p) { CNLoginServer::CNLoginServer(uint16_t p) {
serverType = "login"; serverType = "login";
port = p; port = p;
@@ -285,10 +292,29 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
INITSTRUCT(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC, resp); INITSTRUCT(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC, resp);
int errorCode = 0; int errorCode = 0;
if (!CNLoginServer::isCharacterNameGood(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
errorCode = 4; std::string firstName = AUTOU16TOU8(save->szFirstName);
} else if (!Database::isNameFree(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) { std::string lastName = AUTOU16TOU8(save->szLastName);
errorCode = 1; int nameCheck = 0;
// if FNCode isn't 0, it's a wheel name
if (save->iFNCode != 0) {
if (!CNLoginServer::isNameWheelNameGood(save->iFNCode, save->iMNCode, save->iLNCode, firstName, lastName)) {
errorCode = 4;
} else {
nameCheck = settings:: APPROVEWHEELNAMES ? 1 : 0;
}
} else {
// custom name
nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0;
}
if (errorCode == 0) {
if (!CNLoginServer::isCharacterNameGood(firstName, lastName)) {
errorCode = 4;
} else if (!Database::isNameFree(firstName, lastName)) {
errorCode = 1;
}
} }
if (errorCode != 0) { if (errorCode != 0) {
@@ -306,12 +332,19 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
if (!Database::isSlotFree(loginSessions[sock].userID, save->iSlotNum)) if (!Database::isSlotFree(loginSessions[sock].userID, save->iSlotNum))
return invalidCharacter(sock); return invalidCharacter(sock);
resp.iPC_UID = Database::createCharacter(save, loginSessions[sock].userID); resp.iPC_UID = Database::createCharacter(save->iSlotNum, loginSessions[sock].userID, firstName.c_str(), lastName.c_str(), nameCheck);
// if query somehow failed // if query somehow failed
if (resp.iPC_UID == 0) { if (resp.iPC_UID == 0) {
std::cout << "[WARN] Login Server: Database failed to create new character!" << std::endl; std::cout << "[WARN] Login Server: Database failed to create new character!" << std::endl;
return invalidCharacter(sock); return invalidCharacter(sock);
} }
// fire name check event if needed
if (nameCheck != 1) {
std::string namereq = std::to_string(resp.iPC_UID) + " " + firstName + " " + lastName;
Monitor::namereqs.push_back(namereq);
}
resp.iSlotNum = save->iSlotNum; resp.iSlotNum = save->iSlotNum;
resp.iGender = save->iGender; resp.iGender = save->iGender;
@@ -327,7 +360,9 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
DEBUGLOG( DEBUGLOG(
std::cout << "Login Server: new character created" << std::endl; std::cout << "Login Server: new character created" << std::endl;
std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl; std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl;
std::cout << "\tName: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName) << std::endl; std::cout << "\tName: " << firstName << " " << lastName;
if (nameCheck != 1) std::cout << " (pending approval)";
std::cout << std::endl;
) )
} }
@@ -494,11 +529,30 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
auto save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME*)data->buf; auto save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME*)data->buf;
int errorCode = 0; int errorCode = 0;
if (!CNLoginServer::isCharacterNameGood(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
errorCode = 4; std::string firstName = AUTOU16TOU8(save->szFirstName);
std::string lastName = AUTOU16TOU8(save->szLastName);
int nameCheck = 0;
// if FNCode isn't 0, it's a wheel name
if (save->iFNCode != 0) {
if (!CNLoginServer::isNameWheelNameGood(save->iFNCode, save->iMNCode, save->iLNCode, firstName, lastName)) {
errorCode = 4;
} else {
nameCheck = settings::APPROVEWHEELNAMES ? 1 : 0;
}
} else {
// custom name
nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0;
} }
else if (!Database::isNameFree(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
errorCode = 1; if (errorCode == 0) {
if (!CNLoginServer::isCharacterNameGood(firstName, lastName)) {
errorCode = 4;
}
else if (!Database::isNameFree(firstName, lastName)) {
errorCode = 1;
}
} }
if (errorCode != 0) { if (errorCode != 0) {
@@ -513,9 +567,15 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
return; return;
} }
if (!Database::changeName(save, loginSessions[sock].userID)) if (!Database::changeName(save->iPCUID, loginSessions[sock].userID, firstName.c_str(), lastName.c_str(), nameCheck))
return invalidCharacter(sock); return invalidCharacter(sock);
// fire name check event if needed
if (nameCheck != 1) {
std::string namereq = std::to_string(save->iPCUID) + " " + firstName + " " + lastName;
Monitor::namereqs.push_back(namereq);
}
INITSTRUCT(sP_LS2CL_REP_CHANGE_CHAR_NAME_SUCC, resp); INITSTRUCT(sP_LS2CL_REP_CHANGE_CHAR_NAME_SUCC, resp);
resp.iPC_UID = save->iPCUID; resp.iPC_UID = save->iPCUID;
memcpy(resp.szFirstName, save->szFirstName, sizeof(resp.szFirstName)); memcpy(resp.szFirstName, save->szFirstName, sizeof(resp.szFirstName));
@@ -527,8 +587,10 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
sock->sendPacket(resp, P_LS2CL_REP_CHANGE_CHAR_NAME_SUCC); sock->sendPacket(resp, P_LS2CL_REP_CHANGE_CHAR_NAME_SUCC);
DEBUGLOG( DEBUGLOG(
std::cout << "Login Server: Name check success for character [" << save->iPCUID << "]" << std::endl; std::cout << "Login Server: Name change request for character [" << save->iPCUID << "]" << std::endl;
std::cout << "\tNew name: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName) << std::endl; std::cout << "\tNew name: " << firstName << " " << lastName;
if (nameCheck != 1) std::cout << " (pending approval)";
std::cout << std::endl;
) )
} }
@@ -621,6 +683,42 @@ bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tr
return BCrypt::validatePassword(tryPassword, actualPassword); return BCrypt::validatePassword(tryPassword, actualPassword);
} }
bool CNLoginServer::isNameWheelNameGood(int fnCode, int mnCode, int lnCode, std::string& firstName, std::string& lastName) {
if (fnCode >= LoginServer::WheelFirstNames.size()
|| mnCode >= LoginServer::WheelMiddleNames.size()
|| lnCode >= LoginServer::WheelLastNames.size()) {
std::cout << "[WARN] Login Server: Invalid name codes received: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
return false;
}
// client sends 1 if not selected for these. they point to a single blank space. why.
// just change them to 0, which points to an empty string; keeps the code much cleaner
if (mnCode == 1) mnCode = 0;
if (lnCode == 1) lnCode = 0;
std::string firstNameFromWheel = LoginServer::WheelFirstNames[fnCode];
std::string middleNamePart = LoginServer::WheelMiddleNames[mnCode];
std::string lastNamePart = LoginServer::WheelLastNames[lnCode];
if (mnCode != 0 && middleNamePart[middleNamePart.size() - 1] != ' ') {
// If there's a middle name, we need to lowercase the last name
std::transform(lastNamePart.begin(), lastNamePart.end(), lastNamePart.begin(), ::tolower);
}
std::string lastNameFromWheel = middleNamePart + lastNamePart;
if (firstNameFromWheel.empty() || lastNameFromWheel.empty()) {
std::cout << "[WARN] Login Server: Invalid wheel name combo: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
return false;
}
if (firstName != firstNameFromWheel || lastName != lastNameFromWheel) {
std::cout << "[WARN] Login Server: Name wheel mismatch. Expected " << firstNameFromWheel << " " << lastNameFromWheel << ", got " << firstName << " " << lastName << std::endl;
return false;
}
return true;
}
bool CNLoginServer::isCharacterNameGood(std::string Firstname, std::string Lastname) { bool CNLoginServer::isCharacterNameGood(std::string Firstname, std::string Lastname) {
//Allow alphanumeric and dot characters in names(disallows dot and space characters at the beginning of a name) //Allow alphanumeric and dot characters in names(disallows dot and space characters at the beginning of a name)
std::regex firstnamecheck(R"(((?! )(?!\.)[a-zA-Z0-9]*\.{0,1}(?!\.+ +)[a-zA-Z0-9]* {0,1}(?! +))*$)"); std::regex firstnamecheck(R"(((?! )(?!\.)[a-zA-Z0-9]*\.{0,1}(?!\.+ +)[a-zA-Z0-9]* {0,1}(?! +))*$)");

View File

@@ -7,6 +7,12 @@
#include <map> #include <map>
namespace LoginServer {
extern std::vector<std::string> WheelFirstNames;
extern std::vector<std::string> WheelMiddleNames;
extern std::vector<std::string> WheelLastNames;
}
struct CNLoginData { struct CNLoginData {
int userID; int userID;
time_t lastHeartbeat; time_t lastHeartbeat;
@@ -51,6 +57,7 @@ private:
static bool isPasswordGood(std::string& password); static bool isPasswordGood(std::string& password);
static bool isPasswordCorrect(std::string actualPassword, std::string tryPassword); static bool isPasswordCorrect(std::string actualPassword, std::string tryPassword);
static bool isAccountInUse(int accountId); static bool isAccountInUse(int accountId);
static bool isNameWheelNameGood(int fnCode, int mnCode, int lnCode, std::string& firstName, std::string& lastName);
static bool isCharacterNameGood(std::string Firstname, std::string Lastname); static bool isCharacterNameGood(std::string Firstname, std::string Lastname);
static bool isAuthMethodAllowed(AuthMethod authMethod); static bool isAuthMethodAllowed(AuthMethod authMethod);
static bool checkUsername(CNSocket* sock, std::string& username); static bool checkUsername(CNSocket* sock, std::string& username);

View File

@@ -14,6 +14,13 @@ static std::mutex sockLock; // guards socket list
static std::list<SOCKET> sockets; static std::list<SOCKET> sockets;
static sockaddr_in address; static sockaddr_in address;
std::vector<std::string> Monitor::chats;
std::vector<std::string> Monitor::bcasts;
std::vector<std::string> Monitor::emails;
std::vector<std::string> Monitor::namereqs;
using namespace Monitor;
static bool transmit(std::list<SOCKET>::iterator& it, char *buff, int len) { static bool transmit(std::list<SOCKET>::iterator& it, char *buff, int len) {
int n = 0; int n = 0;
int sock = *it; int sock = *it;
@@ -99,15 +106,23 @@ outer:
} }
// chat // chat
for (auto& str : Chat::dump) { for (auto& str : chats) {
n = std::snprintf(buff, sizeof(buff), "chat %s\n", str.c_str()); n = std::snprintf(buff, sizeof(buff), "chat %s\n", str.c_str());
if (!transmit(it, buff, n)) if (!transmit(it, buff, n))
goto outer; goto outer;
} }
// announcements
for (auto& str : bcasts) {
n = std::snprintf(buff, sizeof(buff), "bcast %s\n", str.c_str());
if (!transmit(it, buff, n))
goto outer;
}
// emails // emails
for (auto& str : Email::dump) { for (auto& str : emails) {
n = process_email(buff, str); n = process_email(buff, str);
if (!transmit(it, buff, n)) if (!transmit(it, buff, n))
@@ -117,14 +132,24 @@ outer:
goto outer; goto outer;
} }
// name requests
for (auto& str : namereqs) {
n = std::snprintf(buff, sizeof(buff), "namereq %s\n", str.c_str());
if (!transmit(it, buff, n))
goto outer;
}
if (!transmit(it, (char*)"end\n", 4)) if (!transmit(it, (char*)"end\n", 4))
continue; continue;
it++; it++;
} }
Chat::dump.clear(); chats.clear();
Email::dump.clear(); bcasts.clear();
emails.clear();
namereqs.clear();
} }
bool Monitor::acceptConnection(SOCKET fd, uint16_t revents) { bool Monitor::acceptConnection(SOCKET fd, uint16_t revents) {

View File

@@ -3,6 +3,11 @@
#include "core/Core.hpp" #include "core/Core.hpp"
namespace Monitor { namespace Monitor {
extern std::vector<std::string> chats;
extern std::vector<std::string> bcasts;
extern std::vector<std::string> emails;
extern std::vector<std::string> namereqs;
SOCKET init(); SOCKET init();
bool acceptConnection(SOCKET, uint16_t); bool acceptConnection(SOCKET, uint16_t);
}; };

View File

@@ -12,7 +12,8 @@ bool settings::SANDBOX = true;
std::string settings::SANDBOXEXTRAPATH = ""; std::string settings::SANDBOXEXTRAPATH = "";
int settings::LOGINPORT = 23000; int settings::LOGINPORT = 23000;
bool settings::APPROVEALLNAMES = true; bool settings::APPROVEWHEELNAMES = true;
bool settings::APPROVECUSTOMNAMES = true;
bool settings::AUTOCREATEACCOUNTS = true; bool settings::AUTOCREATEACCOUNTS = true;
std::string settings::AUTHMETHODS = "password"; std::string settings::AUTHMETHODS = "password";
int settings::DBSAVEINTERVAL = 240; int settings::DBSAVEINTERVAL = 240;
@@ -89,7 +90,8 @@ void settings::init() {
SANDBOX = reader.GetBoolean("", "sandbox", SANDBOX); SANDBOX = reader.GetBoolean("", "sandbox", SANDBOX);
SANDBOXEXTRAPATH = reader.Get("", "sandboxextrapath", SANDBOXEXTRAPATH); SANDBOXEXTRAPATH = reader.Get("", "sandboxextrapath", SANDBOXEXTRAPATH);
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT); LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
APPROVEALLNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVEALLNAMES); APPROVEWHEELNAMES = reader.GetBoolean("login", "acceptallwheelnames", APPROVEWHEELNAMES);
APPROVECUSTOMNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVECUSTOMNAMES);
AUTOCREATEACCOUNTS = reader.GetBoolean("login", "autocreateaccounts", AUTOCREATEACCOUNTS); AUTOCREATEACCOUNTS = reader.GetBoolean("login", "autocreateaccounts", AUTOCREATEACCOUNTS);
AUTHMETHODS = reader.Get("login", "authmethods", AUTHMETHODS); AUTHMETHODS = reader.Get("login", "authmethods", AUTHMETHODS);
DBSAVEINTERVAL = reader.GetInteger("login", "dbsaveinterval", DBSAVEINTERVAL); DBSAVEINTERVAL = reader.GetInteger("login", "dbsaveinterval", DBSAVEINTERVAL);

View File

@@ -9,7 +9,8 @@ namespace settings {
extern bool SANDBOX; extern bool SANDBOX;
extern std::string SANDBOXEXTRAPATH; extern std::string SANDBOXEXTRAPATH;
extern int LOGINPORT; extern int LOGINPORT;
extern bool APPROVEALLNAMES; extern bool APPROVEWHEELNAMES;
extern bool APPROVECUSTOMNAMES;
extern bool AUTOCREATEACCOUNTS; extern bool AUTOCREATEACCOUNTS;
extern std::string AUTHMETHODS; extern std::string AUTHMETHODS;
extern int DBSAVEINTERVAL; extern int DBSAVEINTERVAL;

52
win-setup.ps1 Normal file
View File

@@ -0,0 +1,52 @@
# Run this first if needed:
# Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
param (
# height of largest column without top bar
[Parameter(Mandatory=$true)]
[string]$protocolVersion
)
$ErrorActionPreference = 'Stop'
# check for vscmd
if ([string]::IsNullOrEmpty($env:VSCMD_VER)) {
Write-Host 'Must be run inside of VS Developer Powershell'
exit 1
}
# check for git
try {
$git_version = git --version
} catch {
Write-Host 'git not installed'
exit 1
}
# setup vcpkg
if (Test-Path -Path 'vcpkg\') {
Write-Host 'vcpkg already setup'
} else {
Write-Host 'Setting up vcpkg...'
git clone "https://github.com/microsoft/vcpkg.git"
}
if (-not (Test-Path -Path 'vcpkg\vcpkg.exe')) {
Write-Host 'Bootstrapping vcpkg...'
Start-Process -Wait -NoNewWindow -FilePath 'vcpkg\bootstrap-vcpkg.bat' -ArgumentList '-disableMetrics'
}
$env:VCPKG_ROOT='' # ignore msvc's vcpkg root, it doesn't work
$vcpkg = (Resolve-Path -Path '.\vcpkg')
Write-Host "vcpkg installed to $vcpkg"
Start-Process -Wait -NoNewWindow -FilePath 'vcpkg\vcpkg.exe' -ArgumentList 'install sqlite3:x64-windows'
# setup cmake project
if (Test-Path -Path 'build\') {
Write-Host 'cmake project already setup';
} else {
Write-Host 'Setting up cmake project...'
Start-Process -Wait -NoNewWindow -FilePath 'cmake' -ArgumentList "-B build -DPROTOCOL_VERSION=$protocolVersion -DCMAKE_TOOLCHAIN_FILE=$vcpkg\scripts\buildsystems\vcpkg.cmake"
}
Write-Host 'Done!'
$sln = (Resolve-Path -Path '.\build\OpenFusion.sln')
Write-Host "Solution file is at $sln"