17 Commits

Author SHA1 Message Date
Juansecu
4e0db77c9d Merge 2096c3c3cc into 6d2f120305 2024-06-23 04:36:53 -07:00
Gent Semaj
6d2f120305 Add missing trade offer packets (#280) 2024-06-22 18:20:59 -05:00
Juansecu
2096c3c3cc BUILD - Optimize Dockerfile by using alpine as base image 2024-06-08 21:55:03 -05:00
Juansecu
51615db230 CD - Add GitHub Action to push Docker image to Docker Hub (#275)
* CD - Add GitHub Action to push Docker image to Docker Hub

* BUILD - Reduce size of image by using slim version of Debian

* CI - Modify Push Docker Image action to build and push Docker image to linux/amd64 and linux/arm64 architectures
2024-05-15 10:17:33 -05:00
CakeLancelot
233d21ecd7 Fix copy/paste error in docker-compose.yml 2024-05-07 23:01:09 -05:00
Gent Semaj
54327b0c23 Docker improvements (#274) 2024-05-07 22:11:14 -05:00
CakeLancelot
fa8c1e73d1 Fix a few compiler warnings and formatting for DB startup message (#272)
Note that the warning in TableData.cpp only seems to occur on clang (deprecated-declarations)

Database account/player count message:
Before: [INFO] Database in operation : Found 1 account(s) and 2 player(s)s
After: [INFO] Database in operation: Found 1 account and 2 players
2024-03-31 13:02:39 -05:00
CakeLancelot
aeac57ebf7 Remove duplicate headers from Makefile 2024-03-31 02:48:18 -05:00
CakeLancelot
632406e93b Bump nlohmann JSON to 3.11.3 (#271)
I've checked all the changelogs from the version we were using (3.9.1) to the latest, and there should not be any breaking changes or deprecated functionality.
The server also compiled fine and I did not encounter any issues after a brief play session.
2024-03-29 21:50:55 -05:00
837f109752 Add missing mutex 2024-02-03 03:13:43 -05:00
c11cfebdb1 Fix account and player counts on startup 2024-02-03 03:13:24 -05:00
20367d77f0 Bump copyright date 2024-02-02 22:01:38 -05:00
8d04f31c61 Default PROTOCOL_VERSION to 104 2024-02-02 21:58:37 -05:00
FinnHornhoover
44560a46b7 Rank Calculation Out-of-Bounds Fix (#268) 2023-12-24 02:49:51 +01:00
gsemaj
21d280147c Shard crash fixes 2023-12-19 13:23:58 -05:00
FinnHornhoover
b765821552 added option to disable score capping 2023-12-19 13:18:43 -05:00
e61682dfb2 fix: out-of-bounds index in itemDeleteHandler() 2023-12-18 04:41:53 -06:00
31 changed files with 7249 additions and 7870 deletions

View File

@@ -1 +0,0 @@
version.h

38
.github/workflows/push-docker-image.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Push Docker Image
on:
release:
types: [published]
jobs:
push-docker-image:
name: Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
platforms:
- linux/amd64
- linux/arm64
steps:
- uses: actions/checkout@v4
- name: Retrieve major version
uses: winterjung/split@v2
id: split
with:
msg: ${{ github.ref_name }}
separator: .
- name: Log in to registry
uses: docker/login-action@v3
with:
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
- name: Build and push the Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: ${{ matrix.platforms }}
push: true
tags: ${{ secrets.DOCKERHUB_REPOSITORY }}:${{ github.ref_name }},${{ secrets.DOCKERHUB_REPOSITORY }}:${{ steps.split.outputs._0 }},${{ secrets.DOCKERHUB_REPOSITORY }}:latest

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "tdata"]
path = tdata
url = https://github.com/OpenFusionProject/tabledata.git
[submodule "vendor/Lua"]
path = vendor/Lua
url = https://github.com/walterschell/Lua.git

View File

@@ -34,28 +34,20 @@ else()
set(BIN_NAME fusion)
endif()
# add lua
option(LUA_BUILD_COMPILER OFF)
option(LUA_ENABLE_SHARED OFF)
option(LUA_ENABLE_TESTING OFF)
add_subdirectory(vendor/Lua)
include_directories(src vendor)
file(GLOB_RECURSE VENDOR_SOURCES vendor/bcrypt/**.[ch] vendor/mingw/**.h vendor/INIReader.hpp vendor/JSON.hpp)
file(GLOB_RECURSE SOURCES src/**.[ch]pp version.h)
file(GLOB_RECURSE SOURCES src/**.[ch]pp vendor/**.[ch]pp vendor/**.[ch] version.h)
configure_file(version.h.in ${CMAKE_SOURCE_DIR}/version.h @ONLY)
add_executable(openfusion ${SOURCES} ${VENDOR_SOURCES})
add_executable(openfusion ${SOURCES})
set_target_properties(openfusion PROPERTIES OUTPUT_NAME ${BIN_NAME})
# find sqlite3 and use it
find_package(SQLite3 REQUIRED)
target_include_directories(openfusion PRIVATE ${SQLite3_INCLUDE_DIRS})
target_link_libraries(openfusion PRIVATE lua_static ${SQLite3_LIBRARIES})
target_link_libraries(openfusion PRIVATE ${SQLite3_LIBRARIES})
# Makes it so config, tdata, etc. get picked up when starting via the debugger in VS
set_property(TARGET openfusion PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")

View File

@@ -1,21 +1,37 @@
FROM debian:latest
# build
FROM alpine:3 as build
WORKDIR /usr/src/app
RUN apt-get -y update && apt-get install -y \
RUN apk update && apk upgrade && apk add \
linux-headers \
git \
clang \
clang18 \
make \
libsqlite3-dev
sqlite-dev
COPY . ./
COPY src ./src
COPY vendor ./vendor
COPY .git ./.git
COPY Makefile CMakeLists.txt version.h.in ./
RUN sed -i 's/^CC=clang$/&-18/' Makefile
RUN sed -i 's/^CXX=clang++$/&-18/' Makefile
RUN make -j8
# tabledata should be copied from the host;
# clone it there before building the container
#RUN git submodule update --init --recursive
# prod
FROM alpine:3
CMD ["./bin/fusion"]
WORKDIR /usr/src/app
LABEL Name=openfusion Version=0.0.1
RUN apk update && apk upgrade && apk add \
libstdc++ \
sqlite-dev
COPY --from=build /usr/src/app/bin/fusion /bin/fusion
COPY sql ./sql
CMD ["/bin/fusion"]
LABEL Name=openfusion Version=0.0.2

View File

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

View File

@@ -95,8 +95,6 @@ CXXHDR=\
vendor/bcrypt/BCrypt.hpp\
vendor/INIReader.hpp\
vendor/JSON.hpp\
vendor/INIReader.hpp\
vendor/JSON.hpp\
src/Buffs.hpp\
src/Chat.hpp\
src/CustomCommands.hpp\

View File

@@ -66,6 +66,9 @@ motd=Welcome to OpenFusion!
# location of the database
#dbpath=database.db
# should there be a score cap for infected zone races?
#izracescorecapped=true
# should tutorial flags be disabled off the bat?
disablefirstuseflag=true

View File

@@ -6,6 +6,10 @@ services:
build:
context: .
dockerfile: ./Dockerfile
volumes:
- ./config.ini:/usr/src/app/config.ini
- ./database.db:/usr/src/app/database.db
- ./tdata:/usr/src/app/tdata
ports:
- "23000:23000"
- "23001:23001"

View File

@@ -16,8 +16,9 @@ EntityRef::EntityRef(CNSocket *s) {
EntityRef::EntityRef(int32_t i) {
id = i;
assert(NPCManager::NPCs.find(id) != NPCManager::NPCs.end());
kind = NPCManager::NPCs[id]->kind;
kind = EntityKind::INVALID;
if (NPCManager::NPCs.find(id) != NPCManager::NPCs.end())
kind = NPCManager::NPCs[id]->kind;
}
bool EntityRef::isValid() const {

View File

@@ -64,6 +64,9 @@ static void attachGroupData(std::vector<EntityRef>& pcs, std::vector<EntityRef>&
}
void Groups::addToGroup(Group* group, EntityRef member) {
if (group == nullptr)
return;
if (member.kind == EntityKind::PLAYER) {
Player* plr = PlayerManager::getPlayer(member.sock);
plr->group = group;
@@ -109,6 +112,9 @@ void Groups::addToGroup(Group* group, EntityRef member) {
}
bool Groups::removeFromGroup(Group* group, EntityRef member) {
if (group == nullptr)
return false;
if (member.kind == EntityKind::PLAYER) {
Player* plr = PlayerManager::getPlayer(member.sock);
plr->group = nullptr; // no dangling pointers here muahaahahah
@@ -168,6 +174,9 @@ bool Groups::removeFromGroup(Group* group, EntityRef member) {
}
void Groups::disbandGroup(Group* group) {
if (group == nullptr)
return;
// remove everyone from the group!!
bool done = false;
while(!done) {
@@ -252,6 +261,9 @@ static void leaveGroup(CNSocket* sock, CNPacketData* data) {
}
void Groups::sendToGroup(Group* group, void* buf, uint32_t type, size_t size) {
if (group == nullptr)
return;
auto players = group->filter(EntityKind::PLAYER);
for (EntityRef ref : players) {
ref.sock->sendPacket(buf, type, size);
@@ -259,6 +271,9 @@ void Groups::sendToGroup(Group* group, void* buf, uint32_t type, size_t size) {
}
void Groups::sendToGroup(Group* group, EntityRef excluded, void* buf, uint32_t type, size_t size) {
if (group == nullptr)
return;
auto players = group->filter(EntityKind::PLAYER);
for (EntityRef ref : players) {
if(ref != excluded) ref.sock->sendPacket(buf, type, size);
@@ -294,6 +309,9 @@ void Groups::groupTickInfo(CNSocket* sock) {
void Groups::groupKick(Group* group, EntityRef ref) {
if (group == nullptr)
return;
// if you are the group leader, destroy your own group and kick everybody
if (group->members[0] == ref) {
disbandGroup(group);

View File

@@ -416,6 +416,9 @@ static void itemDeleteHandler(CNSocket* sock, CNPacketData* data) {
Player* plr = PlayerManager::getPlayer(sock);
if (itemdel->iSlotNum < 0 || itemdel->iSlotNum >= AINVEN_COUNT)
return; // sanity check
resp.eIL = itemdel->eIL;
resp.iSlotNum = itemdel->iSlotNum;

View File

@@ -386,6 +386,9 @@ static void taskStart(CNSocket* sock, CNPacketData* data) {
static void taskEnd(CNSocket* sock, CNPacketData* data) {
sP_CL2FE_REQ_PC_TASK_END* missionData = (sP_CL2FE_REQ_PC_TASK_END*)data->buf;
if (Missions::Tasks.find(missionData->iTaskNum) == Missions::Tasks.end())
return;
TaskData* task = Missions::Tasks[missionData->iTaskNum];
// handle timed mission failure

View File

@@ -110,7 +110,7 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) {
}
static void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill) {
if (skill->iNanoID >= NANO_COUNT)
if (skill == nullptr || skill->iNanoID >= NANO_COUNT || skill->iNanoID < 0)
return;
Player *plr = PlayerManager::getPlayer(sock);

View File

@@ -106,10 +106,11 @@ static void racingEnd(CNSocket* sock, CNPacketData* data) {
int timeDiff = now - epRace.startTime;
int podsCollected = epRace.collectedRings.size();
int score = std::min(epInfo.maxScore, (int)std::exp(
int score = std::exp(
(epInfo.podFactor * podsCollected) / epInfo.maxPods
- (epInfo.timeFactor * timeDiff) / epInfo.maxTime
+ epInfo.scaleFactor));
+ epInfo.scaleFactor);
score = (settings::IZRACESCORECAPPED && score > epInfo.maxScore) ? epInfo.maxScore : score;
int fm = (1.0 + std::exp(epInfo.scaleFactor - 1.0) * epInfo.podFactor * podsCollected) / epInfo.maxPods;
// we submit the ranking first...
@@ -132,8 +133,9 @@ static void racingEnd(CNSocket* sock, CNPacketData* data) {
std::vector<int>* rankRewards = &EPRewards[epInfo.EPID].second;
// top ranking
int maxRank = rankScores->size() - 1;
int topRank = 0;
while (rankScores->at(topRank) > topRankingPlayer.Score)
while (topRank < maxRank && rankScores->at(topRank) > topRankingPlayer.Score)
topRank++;
resp.iEPTopRank = topRank + 1;
@@ -143,7 +145,7 @@ static void racingEnd(CNSocket* sock, CNPacketData* data) {
// this ranking
int rank = 0;
while (rankScores->at(rank) > postRanking.Score)
while (rank < maxRank && rankScores->at(rank) > postRanking.Score)
rank++;
resp.iEPRank = rank + 1;

View File

@@ -610,7 +610,7 @@ static void loadDrops(json& dropData) {
if (rankScores.size() != 5 || rankScores.size() != rankRewards.size()) {
char buff[255];
sprintf(buff, "Race in EP %d doesn't have exactly 5 score/reward pairs", raceEPID);
snprintf(buff, 255, "Race in EP %d doesn't have exactly 5 score/reward pairs", raceEPID);
throw TableException(std::string(buff));
}

View File

@@ -186,6 +186,36 @@ static void tradeOfferRefusal(CNSocket* sock, CNPacketData* data) {
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
}
static void tradeOfferCancel(CNSocket* sock, CNPacketData* data) {
sP_CL2FE_REQ_PC_TRADE_OFFER_CANCEL* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_CANCEL*)data->buf;
CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
if (otherSock == nullptr)
return;
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_CANCEL, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_CANCEL));
}
static void tradeOfferAbort(CNSocket* sock, CNPacketData* data) {
sP_CL2FE_REQ_PC_TRADE_OFFER_ABORT* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_ABORT*)data->buf;
CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
if (otherSock == nullptr)
return;
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_ABORT, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_ABORT));
}
static void tradeConfirm(CNSocket* sock, CNPacketData* data) {
sP_CL2FE_REQ_PC_TRADE_CONFIRM* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM*)data->buf;
@@ -430,6 +460,8 @@ void Trading::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER, tradeOffer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT, tradeOfferAccept);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL, tradeOfferRefusal);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_CANCEL, tradeOfferCancel);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ABORT, tradeOfferAbort);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM, tradeConfirm);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL, tradeConfirmCancel);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, tradeRegisterItem);

View File

@@ -50,13 +50,15 @@ time_t getTime();
time_t getTimestamp();
void terminate(int);
// The PROTOCOL_VERSION definition is defined by the build system.
// The PROTOCOL_VERSION definition can be defined by the build system.
#if !defined(PROTOCOL_VERSION)
#define PROTOCOL_VERSION 104
#endif
#if PROTOCOL_VERSION == 104
#include "structs/0104.hpp"
#elif PROTOCOL_VERSION == 728
#include "structs/0728.hpp"
#elif PROTOCOL_VERSION == 104
#include "structs/0104.hpp"
#elif PROTOCOL_VERSION == 1013
#include "structs/1013.hpp"
#else

View File

@@ -226,10 +226,11 @@ static void createTables() {
static int getTableSize(std::string tableName) {
std::lock_guard<std::mutex> lock(dbCrit); // XXX
const char* sql = "SELECT COUNT(*) FROM ?";
// you aren't allowed to bind the table name
const char* sql = "SELECT COUNT(*) FROM ";
tableName.insert(0, sql);
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, tableName.c_str(), -1, NULL);
sqlite3_prepare_v2(db, tableName.c_str(), -1, &stmt, NULL);
sqlite3_step(stmt);
int result = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
@@ -266,17 +267,17 @@ void Database::open() {
checkMetaTable();
createTables();
std::cout << "[INFO] Database in operation ";
std::cout << "[INFO] Database in operation";
int accounts = getTableSize("Accounts");
int players = getTableSize("Players");
std::string message = "";
if (accounts > 0) {
message += ": Found " + std::to_string(accounts) + " Account";
message += ": Found " + std::to_string(accounts) + " account";
if (accounts > 1)
message += "s";
}
if (players > 0) {
message += " and " + std::to_string(players) + " Player Character";
message += " and " + std::to_string(players) + " player";
if (players > 1)
message += "s";
}

View File

@@ -208,6 +208,8 @@ void Database::addBlock(int playerId, int blockedPlayerId) {
}
void Database::removeBlock(int playerId, int blockedPlayerId) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
DELETE FROM Blocks
WHERE PlayerID = ? AND BlockedPlayerID = ?;

View File

@@ -1,30 +0,0 @@
#include "lua/Manager.hpp"
#include <iostream>
using namespace LuaManager;
static lua_State *globalState = nullptr;
void LuaManager::init() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
static LuaThread *startScript(const char *script) {
lua_State *L = lua_newthread(globalState);
LuaThread *T = new LuaThread(L, luaL_ref(L, LUA_REGISTRYINDEX));
if (luaL_dostring(L, script)) {
std::cout << "Lua error: " << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
}
return T;
}
void LuaManager::init() {
globalState = luaL_newstate();
luaL_openlibs(globalState);
delete startScript("print(\"Hello from Lua!\")");
}

View File

@@ -1,7 +0,0 @@
#pragma once
#include "lua/Thread.hpp"
namespace LuaManager {
void init();
}

View File

@@ -1,17 +0,0 @@
#pragma once
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
struct LuaThread {
lua_State *L;
int ref;
LuaThread(lua_State *L, int ref) : L(L), ref(ref) {}
~LuaThread() {
luaL_unref(L, LUA_REGISTRYINDEX, ref);
}
};

View File

@@ -24,8 +24,6 @@
#include "Eggs.hpp"
#include "Rand.hpp"
#include "lua/Manager.hpp"
#include "settings.hpp"
#include "sandbox/Sandbox.hpp"
@@ -140,7 +138,6 @@ int main() {
Trading::init();
Database::open();
LuaManager::init();
switch (settings::EVENTMODE) {
case 0: break; // no event

View File

@@ -67,6 +67,9 @@ int settings::MONITORINTERVAL = 5000;
// event mode settings
int settings::EVENTMODE = 0;
// race settings
bool settings::IZRACESCORECAPPED = true;
void settings::init() {
INIReader reader("config.ini");
@@ -111,6 +114,7 @@ void settings::init() {
EVENTMODE = reader.GetInteger("shard", "eventmode", EVENTMODE);
DISABLEFIRSTUSEFLAG = reader.GetBoolean("shard", "disablefirstuseflag", DISABLEFIRSTUSEFLAG);
ANTICHEAT = reader.GetBoolean("shard", "anticheat", ANTICHEAT);
IZRACESCORECAPPED = reader.GetBoolean("shard", "izracescorecapped", IZRACESCORECAPPED);
MONITORENABLED = reader.GetBoolean("monitor", "enabled", MONITORENABLED);
MONITORPORT = reader.GetInteger("monitor", "port", MONITORPORT);
MONITORINTERVAL = reader.GetInteger("monitor", "interval", MONITORINTERVAL);

View File

@@ -1,5 +1,6 @@
#pragma once
#include <stdint.h>
#include <string>
namespace settings {
@@ -13,7 +14,7 @@ namespace settings {
extern std::string SHARDSERVERIP;
extern bool LOCALHOSTWORKAROUND;
extern bool ANTICHEAT;
extern time_t TIMEOUT;
extern int64_t TIMEOUT;
extern int VIEWDISTANCE;
extern bool SIMULATEMOBS;
extern int SPAWN_X;
@@ -38,6 +39,7 @@ namespace settings {
extern int MONITORPORT;
extern int MONITORINTERVAL;
extern bool DISABLEFIRSTUSEFLAG;
extern bool IZRACESCORECAPPED;
void init();
}

14838
vendor/JSON.hpp vendored

File diff suppressed because it is too large Load Diff

1
vendor/Lua vendored

Submodule vendor/Lua deleted from 88246d621a

View File

@@ -22,9 +22,13 @@
#endif
#include <errno.h>
#ifdef _WIN32 || _WIN64
#if defined(_WIN32) || defined(_WIN64)
// On windows we need to generate random bytes differently.
#if defined(_WIN32) && !defined(_WIN64)
typedef __int32 ssize_t;
#elif defined(_WIN32) && defined(_WIN64)
typedef __int64 ssize_t;
#endif
#define BCRYPT_HASHSIZE 60
#include "bcrypt.h"
@@ -117,7 +121,7 @@ int bcrypt_gensalt(int factor, char salt[BCRYPT_HASHSIZE])
char *aux;
// Note: Windows does not have /dev/urandom sadly.
#ifdef _WIN32 || _WIN64
#if defined(_WIN32) || defined(_WIN64)
HCRYPTPROV p;
ULONG i;

View File

@@ -51,7 +51,7 @@
#endif
/* Just to make sure the prototypes match the actual definitions */
#ifdef _WIN32 || _WIN64
#if defined(_WIN32) || defined(_WIN64)
#include "crypt_blowfish.h"
#else
#include "crypt_blowfish.h"

View File

@@ -41,7 +41,7 @@
#define __SKIP_GNU
#endif
#ifdef _WIN32 | _WIN64
#if defined(_WIN32) || defined(_WIN64)
#include "ow-crypt.h"
#include "crypt_blowfish.h"
@@ -251,7 +251,7 @@ char *__crypt_gensalt_ra(const char *prefix, unsigned long count,
input, size, output, sizeof(output));
if (retval) {
#ifdef _WIN32 | _WIN64
#if defined(_WIN32) || defined(_WIN64)
retval = _strdup(retval);
#else
retval = strdup(retval);