Skip to content
1 change: 1 addition & 0 deletions config.lua.dist
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ cleanProtectionZones = false
ip = "127.0.0.1"
allowOldProtocol = false
bindOnlyGlobalAddress = false
useIpv6 = false
loginProtocolPort = 7171
gameProtocolPort = 7172
statusProtocolPort = 7171
Expand Down
11 changes: 10 additions & 1 deletion data/events/scripts/player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,16 @@ function Player:onLookInBattleList(creature, distance)
description = string.format("%s\nPosition: %d, %d, %d", description, position.x, position.y, position.z)

if creature:isPlayer() then
description = string.format("%s\nIP: %s", description, Game.convertIpToString(creature:getIp()))
local ipString = creature:getIpString() or ""
if ipString == "" then
local ipNumber = creature:getIp()
if ipNumber ~= 0 then
ipString = Game.convertIpToString(ipNumber)
else
ipString = "Unavailable"
end
end
description = string.format("%s\nIP: %s", description, ipString)
end
end
self:sendTextMessage(MESSAGE_LOOK, description)
Expand Down
12 changes: 12 additions & 0 deletions data/libs/functions/game.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ function Game.broadcastMessage(message, messageType)
end

function Game.convertIpToString(ip)
if ip == nil then
return ""
end

if type(ip) == "string" then
return ip
end

if type(ip) ~= "number" then
return ""
end

local band = bit.band
local rshift = bit.rshift
return string.format("%d.%d.%d.%d", band(ip, 0xFF), band(rshift(ip, 8), 0xFF), band(rshift(ip, 16), 0xFF), rshift(ip, 24))
Expand Down
11 changes: 10 additions & 1 deletion data/scripts/eventcallbacks/player/on_look.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,16 @@ local function appendAdminDetails(descriptionText, inspectedThing, inspectedPosi
descriptionText = string.format("%s\nSpeed Base: %d\nSpeed: %d", descriptionText, creatureBaseSpeed, creatureCurrentSpeed)

if inspectedThing:isPlayer() then
descriptionText = string.format("%s\nIP: %s", descriptionText, Game.convertIpToString(inspectedThing:getIp()))
local ipString = inspectedThing:getIpString() or ""
if ipString == "" then
local ipNumber = inspectedThing:getIp()
if ipNumber ~= 0 then
ipString = Game.convertIpToString(ipNumber)
else
ipString = "Unavailable"
end
end
descriptionText = string.format("%s\nIP: %s", descriptionText, ipString)
end
end

Expand Down
14 changes: 11 additions & 3 deletions data/scripts/talkactions/gm/info.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ function info.onSay(player, words, param)
end

local targetIp = target:getIp()
local targetIpString = target:getIpString() or ""
local ipDisplay = targetIpString ~= "" and targetIpString or (targetIp ~= 0 and Game.convertIpToString(targetIp) or "Unavailable")

local text = "Player Info: \n\n"
text = text .. "Name: " .. target:getName() .. "\n"
text = text .. "Access: " .. (target:getGroup():getAccess() and "1" or "0") .. "\n"
text = text .. "Speed: " .. target:getSpeed() .. "\n"
text = text .. "Position: " .. string.format("(%0.5d / %0.5d / %0.3d)", target:getPosition().x, target:getPosition().y, target:getPosition().z) .. "\n"
text = text .. "IP: " .. Game.convertIpToString(targetIp) .. "\n\n"
text = text .. "IP: " .. ipDisplay .. "\n\n"

text = text .. "Skills: \n\n"
text = text .. "* Level: " .. target:getLevel() .. "\n"
Expand All @@ -38,8 +40,14 @@ function info.onSay(player, words, param)

local players = {}
for _, targetPlayer in ipairs(Game.getPlayers()) do
if targetPlayer:getIp() == targetIp and targetPlayer ~= target then
players[#players + 1] = targetPlayer:getName() .. " [" .. targetPlayer:getLevel() .. "]"
if targetPlayer ~= target then
if targetIpString ~= "" then
if (targetPlayer:getIpString() or "") == targetIpString then
players[#players + 1] = targetPlayer:getName() .. " [" .. targetPlayer:getLevel() .. "]"
end
elseif targetIp ~= 0 and targetPlayer:getIp() == targetIp then
players[#players + 1] = targetPlayer:getName() .. " [" .. targetPlayer:getLevel() .. "]"
end
end
end

Expand Down
13 changes: 8 additions & 5 deletions data/scripts/talkactions/gm/mc_check.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ function mcCheck.onSay(player, words, param)
local players = Game.getPlayers()
for i = 1, #players do
local tmpPlayer = players[i]
local ip = tmpPlayer:getIp()
if ip ~= 0 then
local list = ipList[ip]
local ipString = tmpPlayer:getIpString() or ""
local ipNumber = tmpPlayer:getIp()
local key = ipString ~= "" and ipString or (ipNumber ~= 0 and Game.convertIpToString(ipNumber) or "")

if key ~= "" then
local list = ipList[key]
if not list then
ipList[ip] = {}
list = ipList[ip]
ipList[key] = {}
list = ipList[key]
end
list[#list + 1] = tmpPlayer
end
Expand Down
1 change: 1 addition & 0 deletions src/config/config_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum ConfigKey_t : uint16_t {
BESTIARY_KILL_MULTIPLIER,
BESTIARY_RATE_CHARM_SHOP_PRICE,
BIND_ONLY_GLOBAL_ADDRESS,
USE_IPV6,
BLACK_SKULL_DURATION,
BOOSTED_BOSS_KILL_BONUS,
BOOSTED_BOSS_LOOT_BONUS,
Expand Down
1 change: 1 addition & 0 deletions src/config/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ bool ConfigManager::load() {
// Info that must be loaded one time (unless we reset the modules involved)
if (!loaded) {
loadBoolConfig(L, BIND_ONLY_GLOBAL_ADDRESS, "bindOnlyGlobalAddress", false);
loadBoolConfig(L, USE_IPV6, "useIpv6", false);
loadBoolConfig(L, DISABLE_LEGACY_RAIDS, "disableLegacyRaids", false);
loadBoolConfig(L, OLD_PROTOCOL, "allowOldProtocol", true);
loadBoolConfig(L, OPTIMIZE_DATABASE, "startupDatabaseOptimization", true);
Expand Down
61 changes: 38 additions & 23 deletions src/creatures/players/management/ban.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,52 @@
#include "database/databasetasks.hpp"
#include "utils/tools.hpp"

bool Ban::acceptConnection(uint32_t clientIP) {
std::scoped_lock<std::recursive_mutex> lockClass(lock);
namespace {
template <typename Map, typename Key>
bool acceptConnectionInternal(Map &connectMap, const Key &clientIP, std::recursive_mutex &lock) {
std::scoped_lock<std::recursive_mutex> lockClass(lock);

const uint64_t currentTime = OTSYS_TIME();

const uint64_t currentTime = OTSYS_TIME();
auto it = connectMap.find(clientIP);
if (it == connectMap.end()) {
connectMap.emplace(clientIP, ConnectBlock(currentTime, 0, 1));
return true;
}

auto it = ipConnectMap.find(clientIP);
if (it == ipConnectMap.end()) {
ipConnectMap.emplace(clientIP, ConnectBlock(currentTime, 0, 1));
ConnectBlock &connectBlock = it->second;
if (connectBlock.blockTime > currentTime) {
connectBlock.blockTime += 250;
return false;
}

const int64_t timeDiff = currentTime - connectBlock.lastAttempt;
connectBlock.lastAttempt = currentTime;
if (timeDiff <= 5000) {
if (++connectBlock.count > 5) {
connectBlock.count = 0;
if (timeDiff <= 500) {
connectBlock.blockTime = currentTime + 3000;
return false;
}
}
} else {
connectBlock.count = 1;
}
return true;
}
} // namespace

ConnectBlock &connectBlock = it->second;
if (connectBlock.blockTime > currentTime) {
connectBlock.blockTime += 250;
bool Ban::acceptConnection(uint32_t clientIP) {
return acceptConnectionInternal(ipConnectMap, clientIP, lock);
}

bool Ban::acceptConnection(const std::string &clientIP) {
if (clientIP.empty()) {
return false;
}

const int64_t timeDiff = currentTime - connectBlock.lastAttempt;
connectBlock.lastAttempt = currentTime;
if (timeDiff <= 5000) {
if (++connectBlock.count > 5) {
connectBlock.count = 0;
if (timeDiff <= 500) {
connectBlock.blockTime = currentTime + 3000;
return false;
}
}
} else {
connectBlock.count = 1;
}
return true;
return acceptConnectionInternal(ipStringConnectMap, clientIP, lock);
}

bool IOBan::isAccountBanned(uint32_t accountId, BanInfo &banInfo) {
Expand Down
6 changes: 6 additions & 0 deletions src/creatures/players/management/ban.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

#pragma once

#include <map>
#include <string>

struct BanInfo {
std::string bannedBy {};
std::string reason {};
Expand All @@ -25,13 +28,16 @@ struct ConnectBlock {
};

using IpConnectMap = std::map<uint32_t, ConnectBlock>;
using IpStringConnectMap = std::map<std::string, ConnectBlock>;

class Ban {
public:
bool acceptConnection(uint32_t clientIP);
bool acceptConnection(const std::string &clientIP);

private:
IpConnectMap ipConnectMap;
IpStringConnectMap ipStringConnectMap;
std::recursive_mutex lock;
};

Expand Down
35 changes: 34 additions & 1 deletion src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9101,7 +9101,40 @@ void Player::disconnect() const {
}

uint32_t Player::getIP() const {
return client ? client->getIP() : 0;
if (!client) {
return 0;
}

if (client->isIPv6Connection()) {
return 0;
}

return client->getIP();
}

bool Player::isDisconnected() const {
if (!client) {
return true;
}

const auto ipString = getIPString();
if (!ipString.empty()) {
return false;
}

if (client->isIPv6Connection()) {
return false;
}

return getIP() == 0;
}

std::string Player::getIPString() const {
if (!client) {
return {};
}

return client->getIPString();
}

void Player::reloadTaskSlot(PreySlot_t slotid) {
Expand Down
5 changes: 2 additions & 3 deletions src/creatures/players/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,9 @@ class Player final : public Creature, public Cylinder, public Bankable {
void disconnect() const;

uint32_t getIP() const;
std::string getIPString() const;

bool isDisconnected() const {
return getIP() == 0;
}
bool isDisconnected() const;

void addContainer(uint8_t cid, const std::shared_ptr<Container> &container);
void closeContainer(uint8_t cid);
Expand Down
7 changes: 5 additions & 2 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9034,7 +9034,9 @@ void Game::playerDebugAssert(uint32_t playerId, const std::string &assertLine, c
// TODO: move debug assertions to database
FILE* file = fopen("client_assertions.txt", "a");
if (file) {
fprintf(file, "----- %s - %s (%s) -----\n", formatDate(time(nullptr)).c_str(), player->getName().c_str(), convertIPToString(player->getIP()).c_str());
const auto ipString = player->getIPString();
std::string ipForLog = ipString.empty() ? convertIPToString(player->getIP()) : ipString;
fprintf(file, "----- %s - %s (%s) -----\n", formatDate(time(nullptr)).c_str(), player->getName().c_str(), ipForLog.c_str());
fprintf(file, "%s\n%s\n%s\n%s\n", assertLine.c_str(), date.c_str(), description.c_str(), comment.c_str());
fclose(file);
}
Expand Down Expand Up @@ -10811,7 +10813,8 @@ void Game::playerCheckActivity(const std::string &playerName, int interval) {
return;
}

if (player->getIP() == 0) {
const auto remoteAddress = player->getIPString();
if (remoteAddress.empty() && player->getIP() == 0) {
g_game().removeDeadPlayer(playerName);
g_logger().info("Player with name '{}' has logged out due to exited in death screen", player->getName());
player->disconnect();
Expand Down
19 changes: 19 additions & 0 deletions src/lua/functions/creatures/player/player_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void PlayerFunctions::init(lua_State* L) {

Lua::registerMethod(L, "Player", "getGuid", PlayerFunctions::luaPlayerGetGuid);
Lua::registerMethod(L, "Player", "getIp", PlayerFunctions::luaPlayerGetIp);
Lua::registerMethod(L, "Player", "getIpString", PlayerFunctions::luaPlayerGetIpString);
Lua::registerMethod(L, "Player", "getAccountId", PlayerFunctions::luaPlayerGetAccountId);
Lua::registerMethod(L, "Player", "getLastLoginSaved", PlayerFunctions::luaPlayerGetLastLoginSaved);
Lua::registerMethod(L, "Player", "getLastLogout", PlayerFunctions::luaPlayerGetLastLogout);
Expand Down Expand Up @@ -681,6 +682,24 @@ int PlayerFunctions::luaPlayerGetIp(lua_State* L) {
return 1;
}

int PlayerFunctions::luaPlayerGetIpString(lua_State* L) {
// player:getIpString()
const auto &player = Lua::getUserdataShared<Player>(L, 1, "Player");
if (!player) {
lua_pushnil(L);
return 1;
}

const auto &ipString = player->getIPString();
if (ipString.empty()) {
lua_pushnil(L);
return 1;
}

Lua::pushString(L, ipString);
return 1;
}

int PlayerFunctions::luaPlayerGetAccountId(lua_State* L) {
// player:getAccountId()
const auto &player = Lua::getUserdataShared<Player>(L, 1, "Player");
Expand Down
1 change: 1 addition & 0 deletions src/lua/functions/creatures/player/player_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class PlayerFunctions {

static int luaPlayerGetGuid(lua_State* L);
static int luaPlayerGetIp(lua_State* L);
static int luaPlayerGetIpString(lua_State* L);
static int luaPlayerGetAccountId(lua_State* L);
static int luaPlayerGetLastLoginSaved(lua_State* L);
static int luaPlayerGetLastLogout(lua_State* L);
Expand Down
Loading
Loading