Hello,
I would like to share the code and necessary changes required to hide your server IP address using cloudflare websockets.
It was mostly vide-coded with a source from:
Cloudflare WebSocket Tunnel - Free for the Open Tibia community
and
WebSockets proxy
So thanks to guys above for publishing it - it would never be done without them.
I have tested it with Tibianic-DLL client from here:
github.com
First of all - it was mostly vibe-coded with Kiro I do not take any responsibility for bugs, wrong written code here. It would be great to maybe improve it with some more skilled users from this forum. I plan to use it on production soon in my own project and with final version maybe you could use it too - to make more secure private servers.
Ok, now the configuration. You need to create a proxied subcomains in cloudflare for example login.domain.com and game.domain.com (I think you can use one). They should point to your real game server ip address. You do not have to use the same ports than I used. Cloudflare have few ports open which you can use.
Next thing will be tunnel application. You should create a systemd unit for this application and run/check it everytime you start your server (you can add it in restarter.sh - I can also attach it if someone is interested).
This application basically catch all the websockets traffic, translate it to TCP which is default tfs network protocol and send this data to the server. So this is really important to have it working all the time when your server is booted up.
Here is tunnel.go file:
You can compile it using below commands:
You need to generate a free certificate on clouflare and replace paths in main function to allow the secure connection:
certFile := "/etc/ssl/cloudflare/cert.pem"
keyFile := "/etc/ssl/cloudflare/key.pem"
You can edit tfs (tested on TVP 772) with this changes to get a real player ip in game (to check multi-clienting, bans etc):
add privates in connections.h:
Edit this two functions in connections.cpp:
and add this functions at the end of connections.cpp file:
Ok, now the client Tibianic-DLL side.
in const.h set global ip to 127.0.0.1 - we do not need a real ip here anymore.
Add your websockets addresses:
in LIB::LIB(){ inside main.cpp add:
I set this ping host to cloudflare - it will not show the user the real ping which should include Client->CloudFlare->tunnel.go->tfs - it will show only the ping to cloudflare. But it is still better then display 0.
in void LIB::Initialize(){ initialize the bridge:
also of course void LIB:
einitialize(){:
inside "void LIB:
nConnectEvent(const struct sockaddr* address){" comment below section:
it would override the ping host.
in main.h add header:
#include "websocketbridge.h"
and below the pinger/cam add:
Then create two new files websocketbridge.cpp and websocketbridge.h inside the network directory:
(attached as a file - it was too big)
For sure you need to add new files to vcxproj:
<ClInclude Include="..\network\websocketbridge.h" />
<ClCompile Include="..\network\websocketbridge.cpp" />
and extend dependencies:
<AdditionalDependencies>mpir.lib;ws2_32.lib;winmm.lib;wininet.lib;winhttp.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
Block ports 7171/7172 on your local firewall and add your websockets ports as open (maybe you can restrict the source addresses to cloudflare only? - did not tested that).
And you should be good to go
This adds new security layer to your server by hiding it behind cloudflare - which for sure have bigger infrastructure then any of us. For sure it will increase the overall player's latency - but in my tests its was not that much (1 player
)
Regards,
Gover
I would like to share the code and necessary changes required to hide your server IP address using cloudflare websockets.
It was mostly vide-coded with a source from:
Cloudflare WebSocket Tunnel - Free for the Open Tibia community
and
WebSockets proxy
So thanks to guys above for publishing it - it would never be done without them.
I have tested it with Tibianic-DLL client from here:
GitHub - SaiyansKing/tibianic-dll: Windows Tibia client 7.72 DLL injection
Windows Tibia client 7.72 DLL injection. Contribute to SaiyansKing/tibianic-dll development by creating an account on GitHub.
First of all - it was mostly vibe-coded with Kiro I do not take any responsibility for bugs, wrong written code here. It would be great to maybe improve it with some more skilled users from this forum. I plan to use it on production soon in my own project and with final version maybe you could use it too - to make more secure private servers.
Ok, now the configuration. You need to create a proxied subcomains in cloudflare for example login.domain.com and game.domain.com (I think you can use one). They should point to your real game server ip address. You do not have to use the same ports than I used. Cloudflare have few ports open which you can use.
Next thing will be tunnel application. You should create a systemd unit for this application and run/check it everytime you start your server (you can add it in restarter.sh - I can also attach it if someone is interested).
This application basically catch all the websockets traffic, translate it to TCP which is default tfs network protocol and send this data to the server. So this is really important to have it working all the time when your server is booted up.
Here is tunnel.go file:
LUA:
package main
import (
"encoding/binary"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
"github.com/gorilla/websocket"
)
// Configuration - CloudFlare compatible ports
const (
GameWsPort = ":2053" // CloudFlare HTTPS port for game
LoginWsPort = ":2096" // CloudFlare HTTPS port for login
LoginTcpHost = "127.0.0.1:7171" // TFS login server (local connection)
GameTcpHost = "127.0.0.1:7172" // TFS game server (local connection)
EnableLogging = true // Enable connection logging
EnablePROXYv2 = true // Send real client IP via PROXYv2 protocol
BufferSize = 327680 // WebSocket buffer size
)
var upgrader = websocket.Upgrader{
ReadBufferSize: BufferSize,
WriteBufferSize: BufferSize,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// packetBufPool recycles packet body buffers across connections
var packetBufPool = sync.Pool{
New: func() any {
buf := make([]byte, 65535)
return &buf
},
}
// wsReader wraps a WebSocket connection and implements io.Reader
type wsReader struct {
conn *websocket.Conn
reader io.Reader
}
func (r *wsReader) Read(p []byte) (int, error) {
for {
if r.reader != nil {
n, err := r.reader.Read(p)
if err == io.EOF {
r.reader = nil
if n > 0 {
return n, nil
}
continue
}
return n, err
}
messageType, reader, err := r.conn.NextReader()
if err != nil {
return 0, err
}
if messageType != websocket.BinaryMessage {
continue
}
r.reader = reader
}
}
// copyTCPtoWS reads complete Tibia packets from TCP and forwards each as a
// single WebSocket binary frame
func copyTCPtoWS(wsConn *websocket.Conn, tcpConn net.Conn) error {
var headerBuf [2]byte
for {
// Read the 2-byte little-endian Tibia packet length
if _, err := io.ReadFull(tcpConn, headerBuf[:]); err != nil {
return err
}
packetLen := binary.LittleEndian.Uint16(headerBuf[:])
if packetLen == 0 {
continue
}
// Borrow a buffer from the pool
bufPtr := packetBufPool.Get().(*[]byte)
if int(packetLen) > len(*bufPtr) {
*bufPtr = make([]byte, packetLen)
}
body := (*bufPtr)[:packetLen]
// Read exactly packetLen bytes
if _, err := io.ReadFull(tcpConn, body); err != nil {
packetBufPool.Put(bufPtr)
return err
}
// Build the complete frame: [len_lo, len_hi, ...body...]
frame := make([]byte, 2+int(packetLen))
copy(frame[0:2], headerBuf[:])
copy(frame[2:], body)
packetBufPool.Put(bufPtr)
// One complete Tibia packet = one WebSocket frame
if err := wsConn.WriteMessage(websocket.BinaryMessage, frame); err != nil {
return err
}
}
}
// Bridge represents a connection bridge between WebSocket client and TCP server
type Bridge struct {
wsConn *websocket.Conn
tcpConn net.Conn
clientIP string
tcpHost string
closeChan chan struct{}
closeOnce sync.Once
}
// NewBridge creates a new bridge instance
func NewBridge(ws *websocket.Conn, clientIP string, tcpHost string) *Bridge {
return &Bridge{
wsConn: ws,
clientIP: clientIP,
tcpHost: tcpHost,
closeChan: make(chan struct{}),
}
}
// Start initializes the bridge
func (b *Bridge) Start() {
logMsg("Bridge", fmt.Sprintf("Connecting to %s for client: %s", b.tcpHost, b.clientIP))
tcpConn, err := net.DialTimeout("tcp", b.tcpHost, 5*time.Second)
if (err != nil) {
logMsg("Error", fmt.Sprintf("Failed to connect to TFS (%s): %v", b.tcpHost, err))
b.Close()
return
}
// Disable Nagle - critical for real-time game traffic
if tcp, ok := tcpConn.(*net.TCPConn); ok {
tcp.SetNoDelay(true)
tcp.SetKeepAlive(true)
tcp.SetKeepAlivePeriod(60 * time.Second)
}
b.tcpConn = tcpConn
logMsg("Bridge", fmt.Sprintf("TCP connected to %s for client: %s", b.tcpHost, b.clientIP))
// Send PROXYv2 header for real IP forwarding to TFS
if EnablePROXYv2 {
if proxyHeader := b.buildPROXYv2Header(); len(proxyHeader) > 0 {
if _, err := b.tcpConn.Write(proxyHeader); err != nil {
logMsg("Error", fmt.Sprintf("Failed to send PROXYv2 header: %v", err))
b.Close()
return
}
logMsg("Proxy", fmt.Sprintf("PROXYv2 header sent for client: %s", b.clientIP))
}
}
// WS → TCP
go func() {
defer b.Close()
_, err := io.Copy(b.tcpConn, &wsReader{conn: b.wsConn})
if err != nil && !b.isClosed() {
logMsg("WS→TCP Error", fmt.Sprintf("%s: %v", b.clientIP, err))
}
}()
// TCP → WS
go func() {
defer b.Close()
err := copyTCPtoWS(b.wsConn, b.tcpConn)
if err != nil && !b.isClosed() {
logMsg("TCP→WS Error", fmt.Sprintf("%s: %v", b.clientIP, err))
}
}()
}
// isClosed reports whether the bridge has been closed
func (b *Bridge) isClosed() bool {
select {
case <-b.closeChan:
return true
default:
return false
}
}
// Close shuts down both sides of the bridge exactly once
func (b *Bridge) Close() {
b.closeOnce.Do(func() {
close(b.closeChan)
if b.wsConn != nil {
b.wsConn.Close()
}
if b.tcpConn != nil {
b.tcpConn.Close()
}
logMsg("Bridge", fmt.Sprintf("Closed connection for client: %s", b.clientIP))
})
}
// buildPROXYv2Header builds a HAProxy PROXYv2 binary protocol header
// so TFS receives the real client IP rather than 127.0.0.1
func (b *Bridge) buildPROXYv2Header() []byte {
if b.clientIP == "" {
return nil
}
ip := net.ParseIP(b.clientIP)
if ip == nil {
logMsg("Warning", fmt.Sprintf("Invalid IP: %s", b.clientIP))
return nil
}
ipv4 := ip.To4()
if ipv4 == nil {
// Try to extract IP from IPv6-mapped IPv4 address
if strings.Contains(b.clientIP, ":") {
parts := strings.Split(b.clientIP, ":")
if len(parts) > 0 {
ip = net.ParseIP(parts[len(parts)-1])
if ip != nil {
ipv4 = ip.To4()
}
}
}
if ipv4 == nil {
logMsg("Warning", fmt.Sprintf("Cannot convert to IPv4: %s", b.clientIP))
return nil
}
}
// Build PROXYv2 header (28 bytes total)
header := make([]byte, 28)
// Signature (12 bytes)
copy(header[0:12], []byte{0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A})
// Version and command (1 byte): Version 2, PROXY command
header[12] = 0x21
// Address family and protocol (1 byte): AF_INET, STREAM
header[13] = 0x11
// Address block length (2 bytes, big endian): 12 bytes for IPv4
binary.BigEndian.PutUint16(header[14:16], 12)
// Source IP (4 bytes): Real client IP
copy(header[16:20], ipv4)
// Destination IP (4 bytes): Placeholder (0.0.0.0)
copy(header[20:24], []byte{0, 0, 0, 0})
// Source port (2 bytes, big endian): Placeholder
binary.BigEndian.PutUint16(header[24:26], 0)
// Destination port (2 bytes, big endian): TFS port
var dstPort uint16
if strings.Contains(b.tcpHost, "7171") {
dstPort = 7171
} else {
dstPort = 7172
}
binary.BigEndian.PutUint16(header[26:28], dstPort)
return header
}
// extractClientIP extracts the real client IP from proxy/CDN headers
func extractClientIP(r *http.Request) string {
// CloudFlare provides the real client IP in this header
if cfIP := r.Header.Get("CF-Connecting-IP"); cfIP != "" {
return cfIP
}
// Standard proxy headers
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
ips := strings.Split(xff, ",")
if len(ips) > 0 {
return strings.TrimSpace(ips[0])
}
}
if xri := r.Header.Get("X-Real-IP"); xri != "" {
return xri
}
// Fallback to remote address
clientIP := r.RemoteAddr
if strings.Contains(clientIP, ":") {
if host, _, err := net.SplitHostPort(clientIP); err == nil {
clientIP = host
}
}
// Remove IPv6 prefix if present
clientIP = strings.TrimPrefix(clientIP, "::ffff:")
if clientIP == "" || clientIP == "127.0.0.1" || clientIP == "::1" {
logMsg("Warning", fmt.Sprintf("Using localhost IP: %s", clientIP))
}
return clientIP
}
// handleWebSocket upgrades the HTTP connection and starts a bridge
func handleWebSocket(tcpHost string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
clientIP := extractClientIP(r)
logMsg("Server", fmt.Sprintf("New connection from: %s → %s", clientIP, tcpHost))
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
logMsg("Error", fmt.Sprintf("WebSocket upgrade failed: %v", err))
return
}
// Configure WebSocket
ws.EnableWriteCompression(false)
ws.SetReadLimit(327680)
bridge := NewBridge(ws, clientIP, tcpHost)
bridge.Start()
}
}
func logMsg(tag, message string) {
if EnableLogging {
log.Printf("[%s] [%s] [%s]\n", time.Now().Format("2006-01-02 15:04:05.000"), tag, message)
}
}
func main() {
// CloudFlare Origin Certificate paths
certFile := "/etc/ssl/cloudflare/cert.pem"
keyFile := "/etc/ssl/cloudflare/key.pem"
gameServer := &http.Server{
Addr: GameWsPort,
Handler: handleWebSocket(GameTcpHost),
}
loginServer := &http.Server{
Addr: LoginWsPort,
Handler: handleWebSocket(LoginTcpHost),
}
// Handle graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
logMsg("Server", "Shutting down...")
gameServer.Close()
loginServer.Close()
os.Exit(0)
}()
logMsg("Server", "WebSocket Tunnel started (HTTPS)")
logMsg("Server", fmt.Sprintf("Login WebSocket: %s → %s", LoginWsPort, LoginTcpHost))
logMsg("Server", fmt.Sprintf("Game WebSocket: %s → %s", GameWsPort, GameTcpHost))
// Start login server with TLS
go func() {
if err := loginServer.ListenAndServeTLS(certFile, keyFile); err != nil && err != http.ErrServerClosed {
log.Fatal("Login server error:", err)
}
}()
// Start game server with TLS
if err := gameServer.ListenAndServeTLS(certFile, keyFile); err != nil && err != http.ErrServerClosed {
log.Fatal("Game server error:", err)
}
}
You can compile it using below commands:
Code:
winget install GoLang.Go
go mod init tunnel
go mod tidy
set GOOS=linux
set GOARCH=amd64
go build -o tunnel tunnel.go
You need to generate a free certificate on clouflare and replace paths in main function to allow the secure connection:
certFile := "/etc/ssl/cloudflare/cert.pem"
keyFile := "/etc/ssl/cloudflare/key.pem"
You can edit tfs (tested on TVP 772) with this changes to get a real player ip in game (to check multi-clienting, bans etc):
add privates in connections.h:
Code:
void parseFirstData(const boost::system::error_code& error, size_t bytes_transferred);
void parseFirstDataPROXYv2Complete(const boost::system::error_code& error, size_t previousBytes);
// PROXYv2 support
bool parsePROXYv2Header();
// PROXYv2 support for real client IP
uint32_t realClientIP = 0;
bool proxyHeaderParsed = false;
Edit this two functions in connections.cpp:
C++:
void Connection::accept()
{
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()), std::placeholders::_1));
// Read first 12 bytes to detect PROXYv2 signature or handle regular packets
// Minimum 2 bytes allows short status packets to work
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer(), 12),
boost::asio::transfer_at_least(2),
std::bind(&Connection::parseFirstData, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
} catch (boost::system::system_error& e) {
std::cout << "[Network error - Connection::accept] " << e.what() << std::endl;
close(FORCE_CLOSE);
}
}
uint32_t Connection::getIP()
{
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
// If we have real IP from PROXYv2 header, use it
if (realClientIP != 0) {
return realClientIP;
}
// Fallback: get IP from socket (for direct connections without proxy)
boost::system::error_code error;
const boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(error);
if (error) {
return 0;
}
return htonl(endpoint.address().to_v4().to_ulong());
}
and add this functions at the end of connections.cpp file:
C++:
void Connection::parseFirstData(const boost::system::error_code& error, size_t bytes_transferred)
{
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
readTimer.cancel();
if (error) {
close(FORCE_CLOSE);
return;
} else if (closed) {
return;
}
// Check if this looks like PROXYv2 header (need at least 12 bytes for signature)
if (!proxyHeaderParsed && bytes_transferred >= 12) {
// Check PROXYv2 signature
static const uint8_t signature[12] = {
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
};
uint8_t* buffer = msg.getBuffer();
if (memcmp(buffer, signature, 12) == 0) {
// This is PROXYv2, we need at least 28 bytes total
if (bytes_transferred < 28) {
// Read remaining bytes to complete PROXYv2 header
size_t remaining = 28 - bytes_transferred;
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()), std::placeholders::_1));
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer() + bytes_transferred, remaining),
std::bind(&Connection::parseFirstDataPROXYv2Complete, shared_from_this(), std::placeholders::_1, bytes_transferred));
} catch (boost::system::system_error& e) {
std::cout << "[Network error - Connection::parseFirstData] " << e.what() << std::endl;
close(FORCE_CLOSE);
}
return;
}
// We have enough bytes, parse PROXYv2 now
if (parsePROXYv2Header()) {
proxyHeaderParsed = true;
// Continue reading the actual Tibia packet
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()), std::placeholders::_1));
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
} catch (boost::system::system_error& e) {
std::cout << "[Network error - Connection::parseFirstData] " << e.what() << std::endl;
close(FORCE_CLOSE);
}
return;
}
}
}
proxyHeaderParsed = true;
// This is a regular Tibia packet, process it normally
uint16_t packetLen = msg.getBuffer()[0] | (msg.getBuffer()[1] << 8);
if (packetLen == 0 || packetLen >= NETWORKMESSAGE_MAXSIZE - 16) {
close(FORCE_CLOSE);
return;
}
// If we have enough data, process the packet
if (bytes_transferred >= packetLen + 2) {
// CRITICAL: NetworkMessage expects data at buffer[INITIAL_BUFFER_POSITION=4]
// but we read to buffer[0]. Move data to correct position.
uint8_t* buffer = msg.getBuffer();
memmove(buffer + 2, buffer, bytes_transferred);
msg.setLength(packetLen + NetworkMessage::HEADER_LENGTH);
parsePacket(boost::system::error_code());
} else {
// Need to read more data
size_t remaining = (packetLen + 2) - bytes_transferred;
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()), std::placeholders::_1));
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer() + bytes_transferred, remaining),
std::bind(&Connection::parsePacket, shared_from_this(), std::placeholders::_1));
} catch (boost::system::system_error& e) {
std::cout << "[Network error - Connection::parseFirstData] " << e.what() << std::endl;
close(FORCE_CLOSE);
}
}
}
void Connection::parseFirstDataPROXYv2Complete(const boost::system::error_code& error, size_t previousBytes)
{
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
readTimer.cancel();
if (error) {
close(FORCE_CLOSE);
return;
} else if (closed) {
return;
}
if (parsePROXYv2Header()) {
proxyHeaderParsed = true;
// Continue reading the actual Tibia packet
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()), std::placeholders::_1));
boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
} catch (boost::system::system_error& e) {
std::cout << "[Network error - Connection::parseFirstDataPROXYv2Complete] " << e.what() << std::endl;
close(FORCE_CLOSE);
}
} else {
close(FORCE_CLOSE);
}
}
bool Connection::parsePROXYv2Header()
{
// PROXYv2 signature
static const uint8_t signature[12] = {
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
};
uint8_t* buffer = msg.getBuffer();
// Check signature
if (memcmp(buffer, signature, 12) != 0) {
return false;
}
// Parse header
uint8_t verCmd = buffer[12];
uint8_t family = buffer[13];
uint16_t len = (buffer[14] << 8) | buffer[15];
// Check version (must be 2) and command (must be PROXY)
if ((verCmd & 0xF0) != 0x20 || (verCmd & 0x0F) != 0x01) {
return false;
}
if (family == 0x11 && len >= 12) { // AF_INET + STREAM
// Extract IPv4 source address (bytes 16-19)
uint32_t srcIP = (buffer[16] << 24) | (buffer[17] << 16) | (buffer[18] << 8) | buffer[19];
realClientIP = ntohl(srcIP);
std::cout << "[PROXYv2] Real client IP: " << convertIPToString(realClientIP) << std::endl;
return true;
} else if (family == 0x00) { // AF_UNSPEC (LOCAL command)
return true;
}
return false;
}
Ok, now the client Tibianic-DLL side.
in const.h set global ip to 127.0.0.1 - we do not need a real ip here anymore.
Add your websockets addresses:
C++:
#define GLOBAL_PORT 7171
// WebSocket Bridge URLs (CloudFlare protected)
#define WEBSOCKET_LOGIN_URL "wss://login.domain.com:2096"
#define WEBSOCKET_GAME_URL "wss://game.domain.com:2053"
in LIB::LIB(){ inside main.cpp add:
C++:
pinger = NULL;
wsBridge = NULL;
// The default host the pinger will ping
m_pingHost = "login.domain.com";
I set this ping host to cloudflare - it will not show the user the real ping which should include Client->CloudFlare->tunnel.go->tfs - it will show only the ping to cloudflare. But it is still better then display 0.
in void LIB::Initialize(){ initialize the bridge:
C++:
// Initialize WebSocket Bridge
wsBridge = new WebSocketBridge();
if(!wsBridge->start(7171, 7172, WEBSOCKET_LOGIN_URL, WEBSOCKET_GAME_URL)){
m_error = std::string("client was unable to start WebSocket bridge!");
}
also of course void LIB:
C++:
// Stop WebSocket Bridge
if(wsBridge){
wsBridge->stop();
delete wsBridge;
wsBridge = NULL;
}
inside "void LIB:
C++:
if(pinger){
m_pingHost = ipbintostr(addr_in->sin_addr.s_addr);
pinger->setHost(g_dll.m_pingHost);
} else {
m_pingHost = ipbintostr(addr_in->sin_addr.s_addr);
}
it would override the ping host.
in main.h add header:
#include "websocketbridge.h"
and below the pinger/cam add:
C++:
Pinger* pinger;
CAM* cam;
WebSocketBridge* wsBridge;
Post automatically merged:
Then create two new files websocketbridge.cpp and websocketbridge.h inside the network directory:
(attached as a file - it was too big)
For sure you need to add new files to vcxproj:
<ClInclude Include="..\network\websocketbridge.h" />
<ClCompile Include="..\network\websocketbridge.cpp" />
and extend dependencies:
<AdditionalDependencies>mpir.lib;ws2_32.lib;winmm.lib;wininet.lib;winhttp.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
Block ports 7171/7172 on your local firewall and add your websockets ports as open (maybe you can restrict the source addresses to cloudflare only? - did not tested that).
And you should be good to go
This adds new security layer to your server by hiding it behind cloudflare - which for sure have bigger infrastructure then any of us. For sure it will increase the overall player's latency - but in my tests its was not that much (1 player
Regards,
Gover
Attachments
-
network.zip4.4 KB · Views: 4 · VirusTotal
Last edited: