mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-26 08:42:42 +00:00
Merge main into nations-ai
This commit is contained in:
@@ -31,6 +31,7 @@ REGION=$1
|
||||
VERSION_TAG="latest"
|
||||
DOCKER_REPO=""
|
||||
ENV=""
|
||||
SSH_KEY=""
|
||||
|
||||
# Set environment-specific variables
|
||||
if [ "$REGION" == "staging" ]; then
|
||||
@@ -38,15 +39,18 @@ if [ "$REGION" == "staging" ]; then
|
||||
SERVER_HOST=$SERVER_HOST_STAGING
|
||||
DOCKER_REPO=$DOCKER_REPO_STAGING
|
||||
ENV="staging"
|
||||
SSH_KEY=$SSH_KEY_STAGING
|
||||
elif [ "$REGION" == "us" ]; then
|
||||
print_header "DEPLOYING TO US ENVIRONMENT"
|
||||
SERVER_HOST=$SERVER_HOST_US
|
||||
DOCKER_REPO=$DOCKER_REPO_PROD # Uses prod Docker repo for alt environment
|
||||
SSH_KEY=$SSH_KEY_PROD
|
||||
ENV="prod"
|
||||
else
|
||||
print_header "DEPLOYING TO EU ENVIRONMENT"
|
||||
SERVER_HOST=$SERVER_HOST_EU
|
||||
DOCKER_REPO=$DOCKER_REPO_PROD
|
||||
SSH_KEY=$SSH_KEY_PROD
|
||||
ENV="prod"
|
||||
fi
|
||||
|
||||
@@ -57,10 +61,11 @@ if [ -z "$SERVER_HOST" ]; then
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
SSH_KEY=${SSH_KEY:-"~/.ssh/id_rsa"} # Use default or override from .env
|
||||
DOCKER_USERNAME=${DOCKER_USERNAME} # Docker Hub username
|
||||
UPDATE_SCRIPT="./update.sh" # Path to your update script
|
||||
REMOTE_UPDATE_SCRIPT="/root/update-openfront.sh" # Where to place the script on server
|
||||
REMOTE_USER="openfront"
|
||||
REMOTE_UPDATE_PATH="/home/$REMOTE_USER"
|
||||
REMOTE_UPDATE_SCRIPT="$REMOTE_UPDATE_PATH/update-openfront.sh" # Where to place the script on server
|
||||
|
||||
# Check if update script exists
|
||||
if [ ! -f "$UPDATE_SCRIPT" ]; then
|
||||
@@ -90,28 +95,23 @@ if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ Failed to push image to Docker Hub. Stopping deployment."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Docker image built and pushed successfully."
|
||||
|
||||
# Step 2: Copy update script to Hetzner server
|
||||
print_header "STEP 2: Copying update script to server"
|
||||
echo "Target: $SERVER_HOST"
|
||||
echo "Target: $REMOTE_USER@$SERVER_HOST"
|
||||
|
||||
# Make sure the update script is executable
|
||||
chmod +x $UPDATE_SCRIPT
|
||||
|
||||
# Copy the update script to the server
|
||||
scp -i $SSH_KEY $UPDATE_SCRIPT $SERVER_HOST:$REMOTE_UPDATE_SCRIPT
|
||||
scp -i $SSH_KEY $UPDATE_SCRIPT $REMOTE_USER@$SERVER_HOST:$REMOTE_UPDATE_SCRIPT
|
||||
|
||||
# Copy environment variables if needed
|
||||
if [ -f .env ]; then
|
||||
scp -i $SSH_KEY .env $SERVER_HOST:/root/.env
|
||||
scp -i $SSH_KEY .env $REMOTE_USER@$SERVER_HOST:$REMOTE_UPDATE_PATH/.env
|
||||
# Secure the .env file
|
||||
ssh -i $SSH_KEY $SERVER_HOST "chmod 600 /root/.env"
|
||||
ssh -i $SSH_KEY $REMOTE_USER@$SERVER_HOST "chmod 600 $REMOTE_UPDATE_PATH/.env"
|
||||
fi
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
@@ -125,7 +125,7 @@ echo "✅ Update script successfully copied to server."
|
||||
print_header "STEP 3: Executing update script on server"
|
||||
|
||||
# Make the script executable on the remote server and execute it with the environment parameter
|
||||
ssh -i $SSH_KEY $SERVER_HOST "chmod +x $REMOTE_UPDATE_SCRIPT && $REMOTE_UPDATE_SCRIPT $REGION $DOCKER_USERNAME $DOCKER_REPO"
|
||||
ssh -i $SSH_KEY $REMOTE_USER@$SERVER_HOST "chmod +x $REMOTE_UPDATE_SCRIPT && $REMOTE_UPDATE_SCRIPT $REGION $DOCKER_USERNAME $DOCKER_REPO"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ Failed to execute update script on server."
|
||||
|
||||
+18
-12
@@ -1,14 +1,20 @@
|
||||
# AWS Configuration
|
||||
AWS_REGION=region-name
|
||||
AWS_ACCOUNT_ID=your-account-id
|
||||
# Server Configuration
|
||||
SERVER_HOST_STAGING=xxx.xxx.xx.xxx
|
||||
SERVER_HOST_EU=xxx.xxx.xxx.xxx
|
||||
SERVER_HOST_US=x.xxx.xxx.xxx
|
||||
SSH_KEY_STAGING=~/.ssh/your-staging-key
|
||||
SSH_KEY_PROD=~/.ssh/your-prod-key
|
||||
|
||||
# ECR (Elastic Container Registry)
|
||||
ECR_REPO_NAME=your-repo-name
|
||||
# Docker Configuration
|
||||
DOCKER_USERNAME=username
|
||||
DOCKER_REPO_PROD=your-prod-repo
|
||||
DOCKER_REPO_STAGING=your-staging-repo
|
||||
DOCKER_TOKEN=your_docker_token
|
||||
|
||||
# EC2 Deployment Hosts
|
||||
EC2_HOST_STAGING=ec2-user@your-staging-ip
|
||||
EC2_HOST_PROD=ec2-user@your-production-ip
|
||||
EC2_KEY=~/.ssh/your-key-file.pem
|
||||
|
||||
# Application Secrets
|
||||
ADMIN_TOKEN=your-admin-token
|
||||
# Admin credentials
|
||||
ADMIN_TOKEN=your_admin_token
|
||||
R2_ACCESS_KEY=your_r2_access_key
|
||||
R2_SECRET_KEY=your_r2_secret_key
|
||||
R2_ACCOUNT_ID=your_r2_account_id
|
||||
R2_PROD_BUCKET=your-prod-bucket
|
||||
R2_STAGING_BUCKET=your-staging-bucket
|
||||
+834
-2
@@ -1,5 +1,5 @@
|
||||
ownerdomain=openfront.io
|
||||
managerdomain=venatus.com
|
||||
managerdomain=adinplay.com
|
||||
#V 01.04.2025 VH
|
||||
#V
|
||||
|
||||
@@ -164,4 +164,836 @@ criteo.com, B-068503, DIRECT
|
||||
appnexus.com, 806, DIRECT, f5ab79cb980f11d1
|
||||
appnexus.com,1908,RESELLER,f5ab79cb980f11d1
|
||||
adinplay.com, FTB, DIRECT
|
||||
venatus.com, 67f90df66f43edab7e84d165, DIRECT
|
||||
venatus.com, 67f90df66f43edab7e84d165, DIRECT
|
||||
|
||||
##################################
|
||||
# AdinPlay.com ads.txt - 2025-04-16
|
||||
##################################
|
||||
|
||||
adinplay.com, OFI, DIRECT
|
||||
|
||||
#Google
|
||||
google.com, pub-3282547114800347, RESELLER, f08c47fec0942fa0
|
||||
|
||||
#Appnexus
|
||||
appnexus.com, 8631, RESELLER, f5ab79cb980f11d1
|
||||
|
||||
#Index
|
||||
indexexchange.com, 186547, RESELLER, 50b1c356f2c5c8fc
|
||||
indexexchange.com, 187218, RESELLER, 50b1c356f2c5c8fc
|
||||
indexexchange.com, 177754, RESELLER, 50b1c356f2c5c8fc
|
||||
indexexchange.com, 196862, RESELLER, 50b1c356f2c5c8fc
|
||||
indexexchange.com, 207014, RESELLER, 50b1c356f2c5c8fc
|
||||
|
||||
|
||||
#Pulsepoint
|
||||
contextweb.com, 561767, RESELLER, 89ff185a4c4e857c
|
||||
|
||||
#Pubmatic
|
||||
pubmatic.com, 156975, RESELLER, 5d62403b186f2ace
|
||||
pubmatic.com, 156857, RESELLER, 5d62403b186f2ace
|
||||
pubmatic.com, 162231, RESELLER, 5d62403b186f2ace
|
||||
|
||||
#OpenX
|
||||
openx.com, 540164985, RESELLER, 6a698e2ec38604c6
|
||||
openx.com, 540010967, RESELLER, 6a698e2ec38604c6
|
||||
openx.com, 540182293, RESELLER, 6a698e2ec38604c6
|
||||
openx.com, 556894440, RESELLER, 6a698e2ec38604c6
|
||||
|
||||
|
||||
#Sovrn
|
||||
sovrn.com, 268781, RESELLER, fafdf38b16bf6b2b
|
||||
lijit.com, 268781, RESELLER, fafdf38b16bf6b2b
|
||||
lijit.com, 268781-eb, DIRECT, fafdf38b16bf6b2b
|
||||
appnexus.com, 1360, RESELLER, f5ab79cb980f11d1
|
||||
openx.com, 538959099, RESELLER, 6a698e2ec38604c6
|
||||
openx.com, 539924617, RESELLER, 6a698e2ec38604c6
|
||||
pubmatic.com, 137711, RESELLER, 5d62403b186f2ace
|
||||
pubmatic.com, 156212, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 17960, RESELLER, 0bfd66d529a55807
|
||||
sovrn.com, 264160, RESELLER, fafdf38b16bf6b2b
|
||||
lijit.com, 264160, RESELLER, fafdf38b16bf6b2b
|
||||
lijit.com, 264160-eb, DIRECT, fafdf38b16bf6b2b
|
||||
smartadserver.com, 4125, RESELLER
|
||||
sharethrough.com,7144eb80,RESELLER
|
||||
|
||||
|
||||
#Oath
|
||||
coxmt.com, 2000067907202, RESELLER
|
||||
pubmatic.com, 156377, RESELLER, 5d62403b186f2ace #banner
|
||||
pubmatic.com, 156078, RESELLER, 5d62403b186f2ace #banner
|
||||
pubmatic.com, 155967, RESELLER, 5d62403b186f2ace #banner
|
||||
openx.com, 537143344, RESELLER, 6a698e2ec38604c6
|
||||
indexexchange.com, 175407, RESELLER, 50b1c356f2c5c8fc
|
||||
|
||||
#Rhythmone
|
||||
rhythmone.com, 1432377581,DIRECT, a670c89d4a324e47
|
||||
rhythmone.com, 665259327, DIRECT, a670c89d4a324e47
|
||||
rhythmone.com, 2451244104, RESELLER, a670c89d4a324e47
|
||||
video.unrulymedia.com, 2451244104, RESELLER
|
||||
video.unrulymedia.com, 1432377581, DIRECT
|
||||
|
||||
|
||||
#Gumgum
|
||||
aolcloud.net,9904,RESELLER
|
||||
appnexus.com,1001,DIRECT,f5ab79cb980f11d1
|
||||
appnexus.com,2758,RESELLER,f5ab79cb980f11d1
|
||||
appnexus.com,3135,DIRECT,f5ab79cb980f11d1
|
||||
bidtellect.com,1407,RESELLER,1c34aa2d85d45e93
|
||||
contextweb.com,558355,RESELLER,89ff185a4c4e857c
|
||||
openx.com,537120563,DIRECT,6a698e2ec38604c6
|
||||
openx.com,537149485,RESELLER,6a698e2ec38604c6
|
||||
google.com,pub-9557089510405422,DIRECT,f08c47fec0942fa0
|
||||
google.com,pub-3848273848634341,RESELLER,f08c47fec0942fa0
|
||||
google.com, pub-7861278482560604, RESELLER, f08c47fec0942fa0
|
||||
rhythmone.com,78519861,RESELLER, a670c89d4a324e47
|
||||
outbrain.com,01a755b08c8c22b15d46a8b753ab6955d4,RESELLER
|
||||
appnexus.com,7597,RESELLER,f5ab79cb980f11d1
|
||||
openx.com,540003333,RESELLER,6a698e2ec38604c6
|
||||
33across.com,0013300001r0t9mAAA,RESELLER
|
||||
|
||||
|
||||
#Amazon
|
||||
aps.amazon.com,53b902f9-cf9c-4605-aec3-2c8ce65042b8,DIRECT
|
||||
gumgum.com,13543,DIRECT,ffdef49475d318a9
|
||||
appnexus.com,8631,DIRECT,f5ab79cb980f11d1
|
||||
indexexchange.com,196862,DIRECT,50b1c356f2c5c8fc
|
||||
pubmatic.com,160006,RESELLER,5d62403b186f2ace
|
||||
pubmatic.com,160096,RESELLER,5d62403b186f2ace
|
||||
rubiconproject.com,18020,RESELLER,0bfd66d529a55807
|
||||
pubmatic.com,162231,DIRECT,5d62403b186f2ace
|
||||
appnexus.com,1908,RESELLER,f5ab79cb980f11d1
|
||||
smaato.com,1100044650,RESELLER,07bcf65f187117b4
|
||||
ad-generation.jp,12474,RESELLER,7f4ea9029ac04e53
|
||||
districtm.io,100962,RESELLER,3fd707be9c4527c3
|
||||
yieldmo.com,2719019867620450718,RESELLER
|
||||
appnexus.com,3663,RESELLER,f5ab79cb980f11d1
|
||||
rhythmone.com,1654642120,RESELLER,a670c89d4a324e47
|
||||
yahoo.com,55029,RESELLER,e1a5b5b6e3255540
|
||||
gumgum.com,14141,RESELLER,ffdef49475d318a9
|
||||
admanmedia.com,726,RESELLER
|
||||
emxdgt.com,2009,RESELLER,1e1d41537f7cad7f
|
||||
appnexus.com,1356,RESELLER,f5ab79cb980f11d1
|
||||
contextweb.com,562541,RESELLER,89ff185a4c4e857c
|
||||
themediagrid.com,JTQKMP,RESELLER,35d5010d7789b49d
|
||||
sovrn.com,375328,RESELLER,fafdf38b16bf6b2b
|
||||
lijit.com,375328,RESELLER,fafdf38b16bf6b2b
|
||||
beachfront.com,14804,RESELLER,e2541279e8e2ca4d
|
||||
improvedigital.com,2050,RESELLER
|
||||
mintegral.com,10043,RESELLER,0aeed750c80d6423
|
||||
sonobi.com,7f5fa520f8,RESELLER,d1a215d9eb5aee9e
|
||||
openx.com,556894440,DIRECT,6a698e2ec38604c6
|
||||
onetag.com,7683ebe7bee7969,DIRECT
|
||||
media.net,8CUZ1MK22,RESELLER
|
||||
sharethrough.com,buaxQzOE,DIRECT,d53b998a7bd4ecd2
|
||||
smartadserver.com,4571,DIRECT,060d053dcf45cbf3
|
||||
mediago.io,045ac24b888bcf59a09731e7f0f2084f,RESELLER
|
||||
adyoulike.com,7463c359225e043c111036d7a29affa5,RESELLER
|
||||
minutemedia.com,01gya4708ddm,RESELLER
|
||||
visiblemeasures.com,1052,RESELLER
|
||||
undertone.com,4205,RESELLER,d954590d0cb265b9
|
||||
admedia.com,AM1601,RESELLER,ae6c32151e71f19d
|
||||
triplelift.com,8472,DIRECT,6c33edb13117fd86
|
||||
kargo.com,8824,RESELLER
|
||||
start.io,123111883,RESELLER
|
||||
connectad.io,455,RESELLER,85ac85a30c93b3e5
|
||||
|
||||
# 33Across
|
||||
rubiconproject.com, 16414, RESELLER, 0bfd66d529a55807 #33Across #hb #tag
|
||||
rubiconproject.com, 21642, RESELLER, 0bfd66d529a55807 #33Across #hb #tag #viewable
|
||||
rubiconproject.com, 21434, RESELLER, 0bfd66d529a55807 #33Across #tag #ebda
|
||||
rubiconproject.com, 21720, RESELLER, 0bfd66d529a55807 #33Across EU #hb #tag
|
||||
pubmatic.com, 156423, RESELLER, 5d62403b186f2ace #33Across #hb #tag
|
||||
pubmatic.com, 158136, RESELLER, 5d62403b186f2ace #33Across EU #hb #tag
|
||||
pubmatic.com, 158569, RESELLER, 5d62403b186f2ace #33Across #tag #ebda
|
||||
appnexus.com, 10239, RESELLER, f5ab79cb980f11d1 #33Across #hb #tag #viewable
|
||||
appnexus.com, 1001, RESELLER, f5ab79cb980f11d1 #33Across #tag
|
||||
appnexus.com, 3135, RESELLER, f5ab79cb980f11d1 #33Across #tag
|
||||
openx.com, 537120563, RESELLER, 6a698e2ec38604c6 #33Across #hb #tag
|
||||
openx.com, 539392223, RESELLER, 6a698e2ec38604c6 #33Across #tag #ebda
|
||||
openx.com, 540995201, RESELLER, 6a698e2ec38604c6 #33Across #hb #tag #viewable
|
||||
adtech.com, 12094, RESELLER #33Across #hb #tag
|
||||
adtech.com, 9993, RESELLER #33Across #tag
|
||||
aol.com, 47594, RESELLER, e1a5b5b6e3255540 #33Across #hb #tag #viewable
|
||||
yahoo.com, 55188, DIRECT, e1a5b5b6e3255540 #33Across #tag #ebda
|
||||
advangelists.com, 8d3bba7425e7c98c50f52ca1b52d3735, RESELLER, 60d26397ec060f98 #33Across #hb #tag
|
||||
sonobi.com, a416546bb7, RESELLER, d1a215d9eb5aee9e #33Across #tag #ebda
|
||||
indexexchange.com, 190966, RESELLER, 50b1c356f2c5c8fc #33Across #tag #ebda
|
||||
indexexchange.com, 183635, RESELLER, 50b1c356f2c5c8fc #33Across #hb #tag #viewable
|
||||
google.com, pub-9557089510405422, RESELLER, f08c47fec0942fa0 #33Across #tag
|
||||
|
||||
#Rubiconproject
|
||||
rubiconproject.com, 15636, RESELLER, 0bfd66d529a55807
|
||||
|
||||
#LockerDome
|
||||
lockerdome.com, 11908041977355520, DIRECT
|
||||
|
||||
#Yield Nexus
|
||||
yieldnexus.com, 1, DIRECT
|
||||
ssp.ynxs.io, 185, DIRECT
|
||||
appnexus.com, 10617, RESELLER, f5ab79cb980f11d1
|
||||
appnexus.com, 9393, RESELLER, f5ab79cb980f11d1
|
||||
advertising.com, 25034, RESELLER
|
||||
sonobi.com, 783272317b, RESELLER, d1a215d9eb5aee9e
|
||||
indexexchange.com, 186684,RESELLER, 50b1c356f2c5c8fc
|
||||
|
||||
#CPM
|
||||
appnexus.com, 9624, RESELLER, f5ab79cb980f11d1
|
||||
adtech.com, 11506, RESELLER
|
||||
yahoo.com, 56896, RESELLER
|
||||
pubmatic.com, 156078, RESELLER, 5d62403b186f2ace
|
||||
advertising.com, 25218, RESELLER #video, US
|
||||
beachfront.com, 9065, RESELLER
|
||||
contextweb.com, 559969, RESELLER, 89ff185a4c4e857c
|
||||
indexexchange.com, 189455, RESELLER, 50b1c356f2c5c8fc
|
||||
advertising.com, 28320, RESELLER
|
||||
richaudience.com, NtMZGaQQTT, RESELLER
|
||||
adform.com, 1942, RESELLER
|
||||
adform.com, 1941, RESELLER
|
||||
adtech.com, 4687, RESELLER
|
||||
aerserv.com, 2750, RESELLER, 2ce496b9f80eb9fa
|
||||
aol.com, 27093, RESELLER
|
||||
aol.com, 46658, RESELLER
|
||||
aolcloud.net, 4687, RESELLER
|
||||
appnexus.com, 2928, RESELLER, f5ab79cb980f11d1
|
||||
contextweb.com, 560520, RESELLER, 89ff185a4c4e857c
|
||||
google.com, pub-9115524111147081, RESELLER, f08c47fec0942fa0
|
||||
google.com, pub-4673227357197067, RESELLER, f08c47fec0942fa0
|
||||
indexexchange.com, 179394, RESELLER, 50b1c356f2c5c8fc
|
||||
lijit.com, 249425, RESELLER, fafdf38b16bf6b2b
|
||||
cpmstar.com, 49818, RESELLER
|
||||
mobfox.com, 74240, RESELLER
|
||||
mobfox.com, 45499, RESELLER
|
||||
openx.com, 539625136, RESELLER, 6a698e2ec38604c6
|
||||
smaato.com, 1100037086, RESELLER
|
||||
smaato.com, 1100000579, RESELLER
|
||||
sovrn.com, 249425, RESELLER, fafdf38b16bf6b2b
|
||||
openx.com, 541079309, RESELLER, 6a698e2ec38604c6
|
||||
openx.com, 541166421, RESELLER, 6a698e2ec38604c6
|
||||
contextweb.com, 562263, RESELLER, 89ff185a4c4e857c
|
||||
districtm.io, 102015, RESELLER, 3fd707be9c4527c3
|
||||
lkqd.net, 304, RESELLER, 59c49fa9598a0117
|
||||
lkqd.com, 304, RESELLER, 59c49fa9598a0117
|
||||
advertising.com, 2694, RESELLER
|
||||
google.com, pub-5781531207509232, RESELLER, f08c47fec0942fa0
|
||||
appnexus.com, 806, RESELLER, f5ab79cb980f11d1
|
||||
freewheel.tv, 211121, RESELLER
|
||||
freewheel.tv, 211129, RESELLER
|
||||
indexexchange.com, 183921, RESELLER, 50b1c356f2c5c8fc
|
||||
openx.com, 540134228, RESELLER, 6a698e2ec38604c6
|
||||
openx.com, 540634629, RESELLER, 6a698e2ec38604c6
|
||||
pubmatic.com, 156715, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 13762, RESELLER, 0bfd66d529a55807
|
||||
smartadserver.com, 3490, RESELLER
|
||||
springserve.com, 550, RESELLER, a24eb641fc82e93d
|
||||
beachfront.com, 4969, RESELLER, e2541279e8e2ca4d
|
||||
advertising.com, 26282, RESELLER
|
||||
pubmatic.com, 157310, RESELLER, 5d62403b186f2ace
|
||||
rhythmone.com, 2968119028, RESELLER, a670c89d4a324e47
|
||||
contextweb.com, 561910, RESELLER, 89ff185a4c4e857c
|
||||
openx.com, 540226160, RESELLER, 6a698e2ec38604c6
|
||||
openx.com, 540255318, RESELLER, 6a698e2ec38604c6
|
||||
ssp.ynxs.io, 185, RESELLER
|
||||
tremorhub.com, hpwve, RESELLER, 1a4e959a1b50034a
|
||||
telaria.com, hpwve, RESELLER, 1a4e959a1b50034a
|
||||
video.unrulymedia.com, UNRX-PUB-29dad46b-9bec-43c7-b950-c59d09cc8c71, RESELLER
|
||||
video.unrulymedia.com, 985572675, RESELLER
|
||||
rhythmone.com, 2864567592, RESELLER, a670c89d4a324e47
|
||||
vidoomy.com, 51019, RESELLER
|
||||
aol.com, 22762, RESELLER
|
||||
freewheel.tv, 872257, RESELLER
|
||||
openx.com, 540804929, RESELLER, 6a698e2ec38604c6
|
||||
emxdgt.com, 1495, RESELLER, 1e1d41537f7cad7f
|
||||
|
||||
#Rubicon
|
||||
rubiconproject.com, 23042, RESELLER, 0bfd66d529a55807
|
||||
rubiconproject.com, 23044, RESELLER, 0bfd66d529a55807
|
||||
|
||||
|
||||
#AMX
|
||||
|
||||
amxrtb.com, 105199469, RESELLER
|
||||
appnexus.com, 12290, RESELLER, f5ab79cb980f11d1
|
||||
appnexus.com, 11786, RESELLER, f5ab79cb980f11d1
|
||||
indexexchange.com, 191503, RESELLER, 50b1c356f2c5c8fc
|
||||
lijit.com, 260380, RESELLER, fafdf38b16bf6b2b
|
||||
sovrn.com, 260380, RESELLER, fafdf38b16bf6b2b
|
||||
pubmatic.com, 158355, RESELLER, 5d62403b186f2ace
|
||||
appnexus.com, 9393, RESELLER, f5ab79cb980f11d1 #Video #Display
|
||||
appnexus.com, 11924, RESELLER, f5ab79cb980f11d1
|
||||
|
||||
#Kueez
|
||||
kueez.com, fe46d13305ce1b89f18a84c52275b7fe, DIRECT
|
||||
appnexus.com, 8826, RESELLER
|
||||
rubiconproject.com, 16920, RESELLER
|
||||
openx.com, 557564833, RESELLER
|
||||
lijit.com, 407406, RESELLER
|
||||
media.net, 8cu4jtrf9, RESELLER
|
||||
pubmatic.com, 162110, RESELLER
|
||||
sharethrough.com, n98xdzel, RESELLER
|
||||
33across.com, 0010b00002odu4haax, RESELLER
|
||||
yieldmo.com, 3133660606033240149, RESELLER
|
||||
onetag.com, 6e053d779444c00, RESELLER
|
||||
video.unrulymedia.com, 3486482593, RESELLER
|
||||
sonobi.com, 4c4fba1717, RESELLER
|
||||
smartadserver.com, 4288, RESELLER
|
||||
zetaglobal.com, 108, RESELLER
|
||||
improvedigital.com, 2106, RESELLER
|
||||
loopme.com, 11576, RESELLER
|
||||
themediagrid.com, uot45z, RESELLER
|
||||
|
||||
|
||||
#Aniview
|
||||
|
||||
aniview.com, 606c5af8b82e996ca965f498, RESELLER, 78b21b97965ec3f8
|
||||
advertising.com, 23089, RESELLER
|
||||
appnexus.com, 12637, RESELLER, f5ab79cb980f11d1
|
||||
appnexus.com, 9382, RESELLER, f5ab79cb980f11d1
|
||||
synacor.com, 82171, RESELLER, e108f11b2cdf7d5b
|
||||
pubmatic.com, 156344, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 13344, RESELLER, 0bfd66d529a55807
|
||||
indexexchange.com, 191740, RESELLER, 50b1c356f2c5c8fc
|
||||
conversantmedia.com, 100195, DIRECT, 03113cd04947736d
|
||||
appnexus.com, 4052, RESELLER, f5ab79cb980f11d1
|
||||
contextweb.com, 561998, RESELLER, 89ff185a4c4e857c
|
||||
pubmatic.com, 158100, RESELLER, 5d62403b186f2ace
|
||||
yahoo.com, 55771, RESELLER, e1a5b5b6e3255540
|
||||
onetag.com, 57e618150c70d90, DIRECT
|
||||
google.com, pub-3769010358500643, RESELLER, f08c47fec0942fa0
|
||||
video.unrulymedia.com, 3350674472, DIRECT
|
||||
rhythmone.com, 3350674472, DIRECT, a670c89d4a324e47
|
||||
google.com, pub-4586415728471297, RESELLER, f08c47fec0942fa0
|
||||
google.com, pub-3565385483761681, DIRECT, f08c47fec0942fa0
|
||||
google.com, pub-5717092533913515, RESELLER, f08c47fec0942fa0
|
||||
smartadserver.com, 2786, DIRECT
|
||||
improvedigital.com, 1147, DIRECT
|
||||
google.com, pub-2930805104418204, RESELLER, f08c47fec0942fa0
|
||||
google.com, pub-4903453974745530, RESELLER, f08c47fec0942fa0
|
||||
richaudience.com, 1ru8dKmJJV, DIRECT
|
||||
advertising.com, 7574, RESELLER
|
||||
appnexus.com, 8233, RESELLER, f5ab79cb980f11d1
|
||||
pubmatic.com, 81564, RESELLER, 5d62403b186f2ace
|
||||
pubmatic.com, 156538, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 13510, RESELLER, 0bfd66d529a55807
|
||||
smartadserver.com, 2640, RESELLER
|
||||
smartadserver.com, 2441, RESELLER
|
||||
yahoo.com, 57857, RESELLER, e1a5b5b6e3255540
|
||||
undertone.com, 4077, DIRECT
|
||||
appnexus.com, 2234, RESELLER, f5ab79cb980f11d1
|
||||
rubiconproject.com, 22412, RESELLER, 0bfd66d529a55807
|
||||
advertising.com, 28650, RESELLER
|
||||
pubmatic.com, 160318, RESELLER, 5d62403b186f2ace
|
||||
pubmatic.com, 160319, RESELLER, 5d62403b186f2ace
|
||||
appnexus.com, 10112, RESELLER, f5ab79cb980f11d1
|
||||
google.com, pub-0679975395820445, RESELLER, f08c47fec0942fa0
|
||||
google.com, pub-9936969251765866, RESELLER, f08c47fec0942fa0
|
||||
|
||||
#Fluct
|
||||
adingo.jp, 25262, RESELLER
|
||||
pubmatic.com, 156313, RESELLER, 5d62403b186f2ace
|
||||
appnexus.com, 7044, RESELLER, f5ab79cb980f11d1
|
||||
pubmatic.com, 158060, RESELLER, 5d62403b186f2ace
|
||||
|
||||
#Conversant
|
||||
conversantmedia.com, 100106, RESELLER, 03113cd04947736d
|
||||
lijit.com, 411121, RESELLER, fafdf38b16bf6b2b #SOVRN
|
||||
admanmedia.com, 2050, RESELLER
|
||||
Appnerve.com, 187287, RESELLER
|
||||
rubiconproject.com, 23644, RESELLER, 0bfd66d529a55807
|
||||
|
||||
|
||||
#OneTag
|
||||
onetag.com, 7683ebe7bee7969, RESELLER
|
||||
onetag.com, 7683ebe7bee7969-OB, RESELLER
|
||||
appnexus.com, 13099, RESELLER, f5ab79cb980f11d1
|
||||
yahoo.com, 58905, RESELLER, e1a5b5b6e3255540
|
||||
rubiconproject.com, 11006, RESELLER, 0bfd66d529a55807
|
||||
smartadserver.com, 4111, RESELLER
|
||||
|
||||
#Media.net
|
||||
media.net, 8CUEHU9Y5, RESELLER
|
||||
openx.com, 537100188, RESELLER, 6a698e2ec38604c6
|
||||
pubmatic.com, 159463, RESELLER, 5d62403b186f2ace
|
||||
emxdgt.com, 1759, RESELLER, 1e1d41537f7cad7f
|
||||
google.com, pub-7439041255533808, RESELLER, f08c47fec0942fa0
|
||||
rubiconproject.com, 19396, RESELLER, 0bfd66d529a55807
|
||||
onetag.com, 5d49f482552c9b6, RESELLER
|
||||
sonobi.com, 83729e979b, RESELLER
|
||||
33across.com, 0010b00002cGp2AAAS, RESELLER, bbea06d9c4d2853c
|
||||
rhythmone.com, 3611299104, RESELLER, a670c89d4a324e47
|
||||
districtm.io, 100600, RESELLER
|
||||
lemmatechnologies.com, 399, RESELLER, 7829010c5bebd1fb #LEMMA
|
||||
e-planning.net,ec771b05828a67fa,RESELLER,c1ba615865ed87b2
|
||||
google.com, pub-9685734445476814, RESELLER, f08c47fec0942fa0
|
||||
|
||||
#EMX Digital
|
||||
emxdgt.com, 2345, RESELLER, 1e1d41537f7cad7f
|
||||
|
||||
|
||||
#The MediaGrid
|
||||
themediagrid.com, B8ZEVT, RESELLER, 35d5010d7789b49d
|
||||
themediagrid.com, 3W8S2K, RESELLER, 35d5010d7789b49d
|
||||
|
||||
#triplelift
|
||||
triplelift.com, 12900, RESELLER, 6c33edb13117fd86
|
||||
triplelift.com, 12900-EB, DIRECT, 6c33edb13117fd86
|
||||
triplelift.com, 13897, DIRECT, 6c33edb13117fd86
|
||||
|
||||
#Sharethrough
|
||||
|
||||
sharethrough.com, buaxQzOE, RESELLER, d53b998a7bd4ecd2
|
||||
sharethrough.com, jvyAFD6e, DIRECT, d53b998a7bd4ecd2
|
||||
pubmatic.com, 156557, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 18694, RESELLER, 0bfd66d529a55807
|
||||
openx.com, 540274407, RESELLER, 6a698e2ec38604c6
|
||||
33across.com, 0013300001kQj2HAAS, RESELLER, bbea06d9c4d2853c
|
||||
smaato.com, 1100047713, RESELLER, 07bcf65f187117b4
|
||||
yahoo.com, 59531, RESELLER, e1a5b5b6e3255540
|
||||
smartadserver.com, 4342, RESELLER
|
||||
smartadserver.com, 4012, RESELLER
|
||||
|
||||
|
||||
#V 15.01.2024 PH
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
adagio.io, 1090, DIRECT # Adagio_0_6
|
||||
rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807 # Adagio_0_6
|
||||
pubmatic.com, 159110, RESELLER, 5d62403b186f2ace # Adagio_0_6
|
||||
improvedigital.com, 1790, RESELLER # Adagio_0_6
|
||||
indexexchange.com, 194558, RESELLER # Adagio_0_6
|
||||
richaudience.com, 1BTOoaD22a, DIRECT # Adagio_0_6
|
||||
33across.com, 0015a00002oUk4aAAC, DIRECT, bbea06d9c4d2853c # Adagio_0_6
|
||||
appnexus.com, 10239, RESELLER, f5ab79cb980f11d1 # Adagio_0_6
|
||||
rubiconproject.com, 16414, RESELLER, 0bfd66d529a55807 # Adagio_0_6
|
||||
lijit.com, 367236, RESELLER, fafdf38b16bf6b2b # Adagio_0_6
|
||||
e-planning.net, 83c06e81531537f4, RESELLER, c1ba615865ed87b2 # Adagio_0_6
|
||||
amxrtb.com, 105199358, DIRECT # AdaptMX_1_6&7
|
||||
indexexchange.com, 191503, RESELLER # AdaptMX_1_6&7
|
||||
appnexus.com, 11786, RESELLER # AdaptMX_1_6&7
|
||||
appnexus.com, 12290, RESELLER # AdaptMX_1_6&7
|
||||
pubmatic.com, 158355, RESELLER, 5d62403b186f2ace # AdaptMX_1_6&7
|
||||
advertising.com, 28305, RESELLER # AdaptMX_1_6&7
|
||||
rubiconproject.com, 23844, RESELLER, 0bfd66d529a55807 # AdaptMX_1_6&7
|
||||
openx.com, 559680764, RESELLER, 6a698e2ec38604c6 # AdaptMX_1_6&7
|
||||
adform.com, 2767, RESELLER # Adform_0_6&7
|
||||
adyoulike.com, c1314a52de718f3c214c00173d2994f9, DIRECT # AdYouLike_0_6
|
||||
pubmatic.com, 160925, RESELLER, 5d62403b186f2ace # AdYouLike_0_6
|
||||
rubiconproject.com, 20736, RESELLER, 0bfd66d529a55807 # AdYouLike_0_6
|
||||
appnexus.com, 7664, RESELLER # AdYouLike_0_6
|
||||
aps.amazon.com,70247b00-ff8f-4016-b3ab-8344daf96e09,DIRECT # Amazon_3_6&7
|
||||
ad-generation.jp,12474,RESELLER # Amazon_3_6&7
|
||||
aniview.com, 5f2063121d82c82557194737, RESELLER, 78b21b97965ec3f8 # Aniview
|
||||
aniview.com, 643f8e74688b10f72307cc24, DIRECT, 78b21b97965ec3f8 # Aniview
|
||||
google.com, pub-6346866704322274, RESELLER, f08c47fec0942fa0 # Aniview
|
||||
pubmatic.com, 160993, RESELLER, 5d62403b186f2ace # Aniview
|
||||
rubiconproject.com, 13918, RESELLER, 0bfd66d529a55807 # Aniview
|
||||
google.com, pub-5717092533913515, RESELLER, f08c47fec0942fa0 # Aniview
|
||||
gannett.com, 22652678936, RESELLER # Aniview
|
||||
richaudience.com, 1ru8dKmJJV, RESELLER # Aniview
|
||||
appnexus.com, 12637, RESELLER, f5ab79cb980f11d1 # Aniview
|
||||
google.com, pub-3565385483761681, RESELLER, f08c47fec0942fa0 # Aniview
|
||||
sharethrough.com, zLsEa05k, RESELLER, d53b998a7bd4ecd2 # Aniview
|
||||
aps.amazon.com, 1ad7261b-91ea-4b6f-b9e9-b83522205b75, RESELLER # Aniview
|
||||
pubmatic.com, 161335, RESELLER, 5d62403b186f2ace # Aniview
|
||||
google.com, pub-7734005103835923, RESELLER, f08c47fec0942fa0 # Aniview
|
||||
openx.com, 559611024, RESELLER, 6a698e2ec38604c6 # Aniview
|
||||
yieldlab.net, 495507, DIRECT # Aniview
|
||||
blockthrough.com, 5643766199222272, DIRECT # Blockthrough
|
||||
appnexus.com, 6979, RESELLER # Blockthrough
|
||||
indexexchange.com, 194341, RESELLER, 50b1c356f2c5c8fc # Blockthrough
|
||||
pubmatic.com, 160377, RESELLER, 5d62403b186f2ace # Blockthrough
|
||||
rubiconproject.com, 23718, RESELLER, 0bfd66d529a55807 # Blockthrough
|
||||
onetag.com, 75804861b76a852, DIRECT # Blockthrough
|
||||
amxrtb.com, 105199664, DIRECT # Blockthrough
|
||||
criteo.com, B-062405, DIRECT, 9fac4a4a87c2a44f # Criteo_0_6&7
|
||||
themediagrid.com, CVQXOH, DIRECT, 35d5010d7789b49d # Criteo_0_6&7
|
||||
cpmstar.com, 53615, DIRECT # CPMSTAR
|
||||
rhythmone.com,1838093862,DIRECT,a670c89d4a324e47 # CPMSTAR
|
||||
video.unrulymedia.com, 1838093862, DIRECT # CPMSTAR
|
||||
pubmatic.com, 160251, DIRECT, 5d62403b186f2ace # CPMSTAR
|
||||
pubmatic.com, 161595, DIRECT, 5d62403b186f2ace # CPMSTAR
|
||||
rubiconproject.com, 23330, DIRECT, 0bfd66d529a55807 # CPMSTAR
|
||||
conversantmedia.com, 41150, DIRECT, 03113cd04947736d # Epsilon
|
||||
adingo.jp, 24379, DIRECT # Fluct_1_6&7
|
||||
freewheel.tv, 211121, DIRECT # Freewheel_0_7
|
||||
freewheel.tv, 211129, RESELLER # Freewheel_0_7
|
||||
google.com, pub-5781531207509232, RESELLER, f08c47fec0942fa0 # Google_AdX_6&7
|
||||
google.com, pub-2553634189837243, RESELLER, f08c47fec0942fa0 # Google_AdX_6&7
|
||||
gumgum.com, 13385, RESELLER, ffdef49475d318a9 # GumGum_JP_0_9_6
|
||||
gumgum.com, 14302, RESELLER, ffdef49475d318a9 # GumGum_JP_0_9_6
|
||||
improvedigital.com, 1012, DIRECT # Improve_0_6&7
|
||||
improvedigital.com, 1640, RESELLER # Improve_1_6
|
||||
improvedigital.com, 2114, RESELLER # Improve_kids_1_6&7
|
||||
indexexchange.com, 183921, DIRECT, 50b1c356f2c5c8fc # Index Exchange_0_6&7
|
||||
indexexchange.com, 188416, DIRECT, 50b1c356f2c5c8fc # Index Exchange_1_6&7
|
||||
indexexchange.com, 193067, DIRECT, 50b1c356f2c5c8fc # Index Exchange_2_6&7
|
||||
indexexchange.com, 194127, DIRECT, 50b1c356f2c5c8fc # Index Exchange_7&4_6&7
|
||||
indexexchange.com, 205972, RESELLER, 50b1c356f2c5c8fc # Index Exchange_Oz
|
||||
indexexchange.com, 206870, RESELLER, 50b1c356f2c5c8fc # Index_EasyConnect
|
||||
iion.io, 10133, DIRECT # iion
|
||||
kargo.com, 8688, DIRECT # Kargo_0_6
|
||||
rubiconproject.com, 17902, RESELLER, 0bfd66d529a55807 # Magnite_1_6&7
|
||||
rubiconproject.com, 13762, RESELLER, 0bfd66d529a55807 # Magnite_0&2_6&7
|
||||
telaria.com,hpwve,RESELLER,1a4e959a1b50034a # Magnite_Streaming
|
||||
tremorhub.com,hpwve,RESELLER,1a4e959a1b50034a # Magnite_Streaming
|
||||
media.net, 8CU8ARTF8, DIRECT # Media.net
|
||||
Media.net, 8CU5786QK, DIRECT # Media.net
|
||||
themediagrid.com, LTW57M, DIRECT, 35d5010d7789b49d # MediaGrid_2_6&7
|
||||
minutemedia.com, 01gerz6y43ck, RESELLER # MinuteMedia_0_6
|
||||
pubmatic.com, 161683, RESELLER, 5d62403b186f2ace # MinuteMedia_0_6
|
||||
appnexus.com, 8381, RESELLER # MinuteMedia_0_6
|
||||
triplelift.com, 6030, RESELLER, 6c33edb13117fd86 # MinuteMedia_0_6
|
||||
33across.com, 0013300001jlr99AAA, RESELLER, bbea06d9c4d2853c # MinuteMedia_0_6
|
||||
nobid.io, 22629800915, DIRECT # Nobid_0_6
|
||||
sonobi.com, 7ad1b9f952, RESELLER, d1a215d9eb5aee9e # Nobid_0_6
|
||||
xandr.com, 12701, RESELLER, f5ab79cb980f11d1 # Nobid_0_6
|
||||
lijit.com, 273657, DIRECT, fafdf38b16bf6b2b # Nobid_0_6
|
||||
onetag.com, 694e68b73971b58, DIRECT # Nobid_0_6
|
||||
yahoo.com, 57872, RESELLER # Nobid_0_6
|
||||
sharethrough.com, UvcAx8IL, DIRECT, d53b998a7bd4ecd2 # Nobid_0_6
|
||||
ogury.com, 086233d2-e8a8-44fc-907b-f0752e1c85de, DIRECT # Ogury_0_6
|
||||
appnexus.com, 11470, RESELLER # Ogury_0_6
|
||||
openx.com, 537144009, RESELLER, 6a698e2ec38604c6 # OpenX_0_6
|
||||
openx.com, 540134228, RESELLER, 6a698e2ec38604c6 # OpenX_0_7
|
||||
openx.com, 540368327, RESELLER, 6a698e2ec38604c6 # OpenX_1_6&7
|
||||
openx.com, 542378302, RESELLER, 6a698e2ec38604c6 # OpenX_2_6&7
|
||||
the-ozone-project.com, ozoneven0005, DIRECT # Ozone_0_6
|
||||
appnexus.com, 9979, RESELLER # Ozone_0_6
|
||||
openx.com, 540731760, RESELLER, 6a698e2ec38604c6 # Ozone_0_6
|
||||
adform.com, 2657, RESELLER, 9f5210a2f0999e32 # Ozone_0_6
|
||||
pubmatic.com, 160557, RESELLER, 5d62403b186f2ace # Ozone_0_6
|
||||
themediagrid.com, WF71T3, DIRECT, 35d5010d7789b49d # Ozone_0_6
|
||||
pgamssp.com, 634dc90283fff00f005151f2, DIRECT # PGAM_0_7
|
||||
freewheel.tv, 1489202, RESELLER # PGAM_0_7
|
||||
freewheel.tv, 1488706, RESELLER # PGAM_0_7
|
||||
video.unrulymedia.com, 5921144960123684292, RESELLER # PGAM_0_7
|
||||
appnexus.com, 9291, RESELLER # PGAM_0_7
|
||||
pubmatic.com, 162623, RESELLER, 5d62403b186f2ace # PGAM_0_7
|
||||
primis.tech, 31136, DIRECT, b6b21d256ef43532 # Primis
|
||||
pubmatic.com, 156595, RESELLER, 5d62403b186f2ace # Primis
|
||||
google.com, pub-1320774679920841, RESELLER, f08c47fec0942fa0 # Primis
|
||||
openx.com, 540258065, RESELLER, 6a698e2ec38604c6 # Primis
|
||||
rubiconproject.com, 20130, RESELLER, 0bfd66d529a55807 # Primis
|
||||
freewheel.tv, 19133, RESELLER, 74e8e47458f74754 # Primis
|
||||
smartadserver.com, 3436, RESELLER, 060d053dcf45cbf3 # Primis
|
||||
indexexchange.com, 191923, RESELLER, 50b1c356f2c5c8fc # Primis
|
||||
adform.com, 2078, RESELLER # Primis
|
||||
Media.net, 8CU695QH7, RESELLER # Primis
|
||||
video.unrulymedia.com, 2338962694, RESELLER # Primis
|
||||
sharethrough.com, flUyJowI, RESELLER, d53b998a7bd4ecd2 # Primis
|
||||
triplelift.com, 8210, RESELLER, 6c33edb13117fd86 # Primis
|
||||
yahoo.com, 59260, RESELLER # Primis
|
||||
pubmatic.com, 159234, RESELLER, 5d62403b186f2ace # PubMatic_0_6&7
|
||||
pubmatic.com, 158940, RESELLER, 5d62403b186f2ace # PubMatic_1_6&7
|
||||
pubmatic.com, 160552, RESELLER, 5d62403b186f2ace # PubMatic_4_7
|
||||
pubmatic.com, 159401, RESELLER, 5d62403b186f2ace # PubMatic_2_6&7
|
||||
pubmatic.com, 163598, RESELLER, 5d62403b186f2ace # Pubmatic_OW
|
||||
richaudience.com, 1XvIoD5o0S, DIRECT # Rich Audience_0_6&7
|
||||
risecodes.com, 5fa94677b2db6a00015b22a9, DIRECT # Rise
|
||||
pubmatic.com, 160295, RESELLER, 5d62403b186f2ace # Rise
|
||||
xandr.com, 14082, RESELLER # Rise
|
||||
rubiconproject.com, 23876, RESELLER, 0bfd66d529a55807 # Rise
|
||||
media.net, 8CUQ6928Q, RESELLER # Rise_Temp
|
||||
sharethrough.com, 5926d422, RESELLER, d53b998a7bd4ecd2 # Rise_Temp
|
||||
sharethrough.com, 31c129df, DIRECT, d53b998a7bd4ecd2 # Sharethrough_0_6&7
|
||||
sharethrough.com, Ip2TfKpa, DIRECT, d53b998a7bd4ecd2 # Sharethrough_1_6&7
|
||||
smartadserver.com, 2161, RESELLER # Showheroes_7_8
|
||||
appnexus.com, 8833, RESELLER, f5ab79cb980f11d1 # Showheroes_7_8
|
||||
smartadserver.com, 3668, RESELLER # Showheroes_7_8
|
||||
freewheel.tv, 1003361, DIRECT # Showheroes_7_8
|
||||
pubmatic.com, 156695, DIRECT, 5d62403b186f2ace # Showheroes_7_8
|
||||
showheroes.com, 6829, RESELLER # Showheroes_7_8
|
||||
smartadserver.com, 3490, DIRECT # Smart AdServer_0&1&2_6&7
|
||||
smartadserver.com, 3490-OB, DIRECT, 060d053dcf45cbf3 # Smart AdServer_0&1&2_6&7
|
||||
smartadserver.com, 4016, DIRECT # Smart AdServer_0&1&2_6&7
|
||||
smartadserver.com, 4074, DIRECT # Smart AdServer_0&1&2_6&7
|
||||
smaato.com, 1100055690, DIRECT, 07bcf65f187117b4 # Smaato
|
||||
smaato.com, 1100004890, DIRECT, 07bcf65f187117b4 # Smaato
|
||||
sonobi.com, 116da9d98c, DIRECT, d1a215d9eb5aee9e # Sonobi_0_6&7
|
||||
sonobi.com, e017850301, DIRECT, d1a215d9eb5aee9e # Sonobi_4_7
|
||||
sovrn.com, 237754, DIRECT, fafdf38b16bf6b2b # Sovrn_0&1&2_6&7
|
||||
lijit.com, 237754, DIRECT, fafdf38b16bf6b2b # Sovrn_0&1&2_6&7
|
||||
lijit.com, 237754-eb, DIRECT, fafdf38b16bf6b2b # Sovrn_1_6&7
|
||||
taboola.com,1422403,DIRECT,c228e6794e811952 # Taboola_6_8
|
||||
triplelift.com, 6059, DIRECT, 6c33edb13117fd86 # Triplelift_0&2_6&7
|
||||
triplelift.com, 6059-EB, DIRECT, 6c33edb13117fd86 # Triplelift_0&2_6&7
|
||||
video.unrulymedia.com, 985572675, DIRECT # Unruly_0&2_7
|
||||
rhythmone.com, 2864567592, DIRECT, a670c89d4a324e47 # Unruly_0&2_7
|
||||
xandr.com, 13799, RESELLER # Unruly
|
||||
sharethrough.com, 6qlnf8SY, RESELLER, d53b998a7bd4ecd2 # Unruly
|
||||
vidazoo.com, 655c85dc63ceeb606a0f365f, DIRECT, b6ada874b4d7d0b2 # Vidazoo
|
||||
pubmatic.com, 159988, RESELLER, 5d62403b186f2ace # Vidazoo
|
||||
rubiconproject.com, 17130, RESELLER, 0bfd66d529a55807 # Vidazoo
|
||||
pubmatic.com, 156512, DIRECT # Wunderkind
|
||||
indexexchange.com, 183753, DIRECT # Wunderkind
|
||||
wunderkind.co, 6438, DIRECT # Wunderkind
|
||||
wunderkind.co, 6449, DIRECT # Wunderkind
|
||||
criteo.com, B-068503, DIRECT # Wunderkind
|
||||
appnexus.com, 806, DIRECT, f5ab79cb980f11d1 # Xandr_0&2_6&7
|
||||
appnexus.com,1908,RESELLER,f5ab79cb980f11d1 # Xandr_0&2_6&7
|
||||
|
||||
|
||||
#Equativ
|
||||
|
||||
smartadserver.com, 4571, RESELLER, 060d053dcf45cbf3
|
||||
smartadserver.com, 4571-OB, RESELLER, 060d053dcf45cbf3
|
||||
smartadserver.com, 4016, RESELLER, 060d053dcf45cbf3 #Global
|
||||
smartadserver.com, 4012, RESELLER, 060d053dcf45cbf3 #EUR
|
||||
smartadserver.com, 4071, RESELLER, 060d053dcf45cbf3 #USD
|
||||
smartadserver.com, 4073, RESELLER, 060d053dcf45cbf3 #BRL
|
||||
smartadserver.com, 4074, RESELLER, 060d053dcf45cbf3 #MXN
|
||||
smartadserver.com, 4247, RESELLER, 060d053dcf45cbf3 #CAD
|
||||
smartadserver.com, 4228, RESELLER, 060d053dcf45cbf3 #USD_CTV
|
||||
pubmatic.com, 156439, RESELLER, 5d62403b186f2ace
|
||||
pubmatic.com, 154037, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 16114, RESELLER, 0bfd66d529a55807
|
||||
openx.com, 537149888, RESELLER, 6a698e2ec38604c6
|
||||
appnexus.com, 3703, RESELLER, f5ab79cb980f11d1
|
||||
loopme.com, 5679, RESELLER, 6c8d5f95897a5a3b
|
||||
xad.com, 958, RESELLER, 81cbf0a75a5e0e9a
|
||||
video.unrulymedia.com, 2564526802, RESELLER
|
||||
smaato.com, 1100044045, RESELLER, 07bcf65f187117b4
|
||||
pubnative.net, 1006576, RESELLER, d641df8625486a7b
|
||||
verve.com, 15503, RESELLER, 0c8f5958fc2d6270
|
||||
adyoulike.com, b4bf4fdd9b0b915f746f6747ff432bde, RESELLER, 4ad745ead2958bf7
|
||||
axonix.com, 57264, RESELLER
|
||||
admanmedia.com, 43, RESELLER
|
||||
sharethrough.com, OAW69Fon, RESELLER, d53b998a7bd4ecd2
|
||||
contextweb.com, 560288, RESELLER, 89ff185a4c4e857c
|
||||
|
||||
#nobid
|
||||
|
||||
nobid.io, 22931676975, DIRECT
|
||||
xandr.com, 11429, RESELLER, f5ab79cb980f11d1
|
||||
sharethrough.com, aRE1degH, RESELLER, d53b998a7bd4ecd2
|
||||
sonobi.com, 7ad1b9f952, RESELLER, d1a215d9eb5aee9e
|
||||
sharethrough.com, UvcAx8IL, RESELLER, d53b998a7bd4ecd2
|
||||
amxrtb.com, 105199579, RESELLER
|
||||
yahoo.com,49648,RESELLER
|
||||
rubiconproject.com, 24434, RESELLER, 0bfd66d529a55807
|
||||
minutemedia.com, 01gerz67grgj, RESELLER
|
||||
pubmatic.com, 161683, RESELLER, 5d62403b186f2ace
|
||||
appnexus.com, 8381, RESELLER, f5ab79cb980f11d1
|
||||
triplelift.com, 6030, RESELLER, 6c33edb13117fd86
|
||||
sonobi.com, 37fbaf262c, RESELLER, d1a215d9eb5aee9e
|
||||
openx.com, 540780517, RESELLER, 6a698e2ec38604c6
|
||||
rubiconproject.com, 17598, RESELLER, 0bfd66d529a55807
|
||||
indexexchange.com, 196326, RESELLER, 50b1c356f2c5c8fc
|
||||
yahoo.com, 59407, RESELLER, e1a5b5b6e3255540
|
||||
sharethrough.com, xz7QjFBY, RESELLER, d53b998a7bd4ecd2
|
||||
inmobi.com,8f261ace12c3486ba2e0d2011cd97976,RESELLER,83e75a7ae333ca9d
|
||||
risecodes.com, 63ea59eef828de0001cf1773, RESELLER
|
||||
inmobi.com, 9e311c7a68e94888aac7fbb4272381e2, RESELLER, 83e75a7ae333ca9d
|
||||
video.unrulymedia.com, 1352466146, RESELLER
|
||||
yahoo.com, 59261, RESELLER, e1a5b5b6e3255540
|
||||
gumgum.com, 13926, RESELLER, ffdef49475d318a9
|
||||
onetag.com, 694e68b73971b58, RESELLER
|
||||
lijit.com, 273657, RESELLER, fafdf38b16bf6b2b
|
||||
sovrn.com, 273657, RESELLER, fafdf38b16bf6b2b
|
||||
mediafuse.com, 389, RESELLER
|
||||
appnexus.com, 9538, RESELLER, f5ab79cb980f11d1
|
||||
yahoo.com, 57872, RESELLER
|
||||
video.unrulymedia.com, 2997140015, RESELLER
|
||||
indexexchange.com, 182257, RESELLER, 50b1c356f2c5c8fc
|
||||
152media.info,152M374,RESELLER
|
||||
appnexus.com, 3153, RESELLER, f5ab79cb980f11d1
|
||||
#media.net_serverside_displayvideo
|
||||
media.net, 8CUV34PJ4, DIRECT
|
||||
sharethrough.com, koRtppYA, RESELLER, d53b998a7bd4ecd2
|
||||
video.unrulymedia.com, 699546687, RESELLER
|
||||
lijit.com, 264726, RESELLER, fafdf38b16bf6b2b
|
||||
onetag.com, 765b4e6bb9c8438, RESELLER
|
||||
amxrtb.com, 105199663, RESELLER
|
||||
yieldmo.com, 2954622693783052507, RESELLER
|
||||
loopme.com, 11556, RESELLER, 6c8d5f95897a5a3b
|
||||
Contextweb.com, 562963, RESELLER, 89ff185a4c4e857c
|
||||
zeta.com, 591, RESELLER
|
||||
disqus.com, 591, RESELLER
|
||||
admanmedia.com, 953, RESELLER
|
||||
smartadserver.com, 4106, RESELLER, 060d053dcf45cbf3
|
||||
imds.tv, 82302, RESELLER, ae6c32151e71f19d
|
||||
improvedigital.com, 2073, RESELLER
|
||||
betweendigital.com, 44808, RESELLER
|
||||
adyoulike.com, 53264963677efeda057eef7db2cb305f, RESELLER
|
||||
freewheel.tv,1577878,RESELLER
|
||||
freewheel.tv,1577888,RESELLER
|
||||
dxkulture.com, 9533, DIRECT, 259726033fc4df0c
|
||||
dxkulture.com, 0098, DIRECT, 259726033fc4df0c
|
||||
adswizz.com,dxkulture,DIRECT
|
||||
adswizz.com,651,DIRECT
|
||||
pubmatic.com,164751,RESELLER,5d62403b186f2ace
|
||||
rubiconproject.com,26094,DIRECT,0bfd66d529a55807
|
||||
zetaglobal.net,790,DIRECT
|
||||
ssp.disqus.com,790,DIRECT
|
||||
video.unrulymedia.com,946176315,RESELLER
|
||||
video.unrulymedia.com, 347774562, RESELLER
|
||||
rubiconproject.com, 15268, RESELLER, 0bfd66d529a55807
|
||||
pubmatic.com, 159277, RESELLER
|
||||
|
||||
#AdaptMX
|
||||
|
||||
amxrtb.com, 105199723, DIRECT
|
||||
appnexus.com, 12290, RESELLER
|
||||
pubmatic.com, 161527, RESELLER
|
||||
rubiconproject.com, 23844, RESELLER
|
||||
|
||||
|
||||
|
||||
# Adagio
|
||||
adagio.io, 1361, RESELLER
|
||||
# Adagio - Magnite
|
||||
rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807
|
||||
# Adagio - Pubmatic
|
||||
pubmatic.com, 159110, RESELLER, 5d62403b186f2ace
|
||||
# Adagio - Improve Digital
|
||||
improvedigital.com, 1790, RESELLER
|
||||
# Adagio - Onetag
|
||||
onetag.com, 6b859b96c564fbe, RESELLER
|
||||
appnexus.com, 13099, RESELLER
|
||||
pubmatic.com, 161593, RESELLER, 5d62403b186f2ace
|
||||
# Adagio - Index Exchange
|
||||
indexexchange.com, 194558, RESELLER
|
||||
# Adagio - 33Across
|
||||
33across.com, 0015a00002oUk4aAAC, RESELLER, bbea06d9c4d2853c
|
||||
yahoo.com, 57289, RESELLER, e1a5b5b6e3255540
|
||||
appnexus.com, 10239, RESELLER, f5ab79cb980f11d1
|
||||
rubiconproject.com, 16414, RESELLER, 0bfd66d529a55807
|
||||
pubmatic.com, 156423, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 21642, RESELLER, 0bfd66d529a55807
|
||||
conversantmedia.com, 100141, RESELLER
|
||||
indexexchange.com, 191973, RESELLER, 50b1c356f2c5c8fc
|
||||
triplelift.com, 12503, RESELLER, 6c33edb13117fd86
|
||||
insticator.com, 4ec3ed85-2830-4174-9f7f-f545620598b9, RESELLER
|
||||
sharethrough.com, Q9IzHdvp, RESELLER, d53b998a7bd4ecd2
|
||||
admanmedia.com, 2216, RESELLER
|
||||
connectad.io, 456, RESELLER, 85ac85a30c93b3e5
|
||||
# Adagio - Equativ
|
||||
smartadserver.com, 3554, RESELLER
|
||||
# Adagio - Sovrn
|
||||
lijit.com, 367236, RESELLER, fafdf38b16bf6b2b
|
||||
# Adagio - Freewheel
|
||||
freewheel.tv, 1568036, RESELLER
|
||||
freewheel.tv, 1568041, RESELLER
|
||||
# Adagio - OpenX
|
||||
openx.com, 558899373, RESELLER, 6a698e2ec38604c6
|
||||
# Adagio - Triplelift
|
||||
triplelift.com, 13482, RESELLER, 6c33edb13117fd86
|
||||
# Adagio - E-Planning
|
||||
e-planning.net, 83c06e81531537f4, RESELLER, c1ba615865ed87b2
|
||||
pubmatic.com, 156631, RESELLER, 5d62403b186f2ace
|
||||
openx.com, 541031350, RESELLER, 6a698e2ec38604c6
|
||||
rubiconproject.com, 12186, RESELLER, 0bfd66d529a55807
|
||||
# Adagio - Nexxen
|
||||
video.unrulymedia.com, 5672421953199218469, RESELLER
|
||||
|
||||
#Freewheel
|
||||
|
||||
freewheel.tv, 1598995, RESELLER
|
||||
freewheel.tv, 1599004, RESELLER
|
||||
|
||||
|
||||
#Pgam
|
||||
|
||||
pgamssp.com, 64661fa49d522e327b0a8b84, DIRECT
|
||||
freewheel.tv, 1489202, RESELLER
|
||||
freewheel.tv, 1488706, RESELLER
|
||||
rubiconproject.com, 24852, RESELLER, 0bfd66d529a55807
|
||||
pubmatic.com, 162623, RESELLER, 5d62403b186f2ace
|
||||
video.unrulymedia.com, 5921144960123684292, RESELLER
|
||||
appnexus.com, 9291, RESELLER, f5ab79cb980f11d1
|
||||
|
||||
|
||||
#Sonobi
|
||||
|
||||
sonobi.com, 3ee2ca3952, RESELLER, d1a215d9eb5aee9e
|
||||
|
||||
|
||||
#Rich Audience
|
||||
|
||||
richaudience.com, kWVs0vbyki, RESELLER
|
||||
appnexus.com, 2928, DIRECT, f5ab79cb980f11d1
|
||||
smartadserver.com, 1999, RESELLER, 060d053dcf45cbf3
|
||||
|
||||
#Ozone
|
||||
|
||||
the-ozone-project.com, OZONEAIP0001, DIRECT
|
||||
appnexus.com, 9979, RESELLER, f5ab79cb980f11d1
|
||||
openx.com, 540731760, RESELLER, 6a698e2ec38604c6
|
||||
adform.com, 2657, RESELLER, 9f5210a2f0999e32
|
||||
pubmatic.com, 160557, RESELLER, 5d62403b186f2ace
|
||||
indexexchange.com, 206233, RESELLER, 50b1c356f2c5c8fc
|
||||
themediagrid.com, 1J3ZI6, DIRECT, 35d5010d7789b49d
|
||||
themediagrid.com, WF71T3, DIRECT, 35d5010d7789b49d
|
||||
|
||||
# OptiDigital
|
||||
optidigital.com,p345,RESELLER
|
||||
pubmatic.com,158939,RESELLER,5d62403b186f2ace
|
||||
rubiconproject.com,20336,RESELLER,0bfd66d529a55807
|
||||
smartadserver.com,3379,RESELLER,060d053dcf45cbf3
|
||||
criteo.com,B-060926,RESELLER,9fac4a4a87c2a44f
|
||||
themediagrid.com,3ETIX5,RESELLER,35d5010d7789b49d
|
||||
triplelift.com,8183,RESELLER,6c33edb13117fd86
|
||||
appnexus.com,12190,RESELLER,f5ab79cb980f11d1
|
||||
onetag.com,806eabb849d0326,RESELLER
|
||||
rtbhouse.com,mSu1piUSmB9TF4AQDGk4,RESELLER
|
||||
33across.com,001Pg00000HMy0YIAT,RESELLER,bbea06d9c4d2853c
|
||||
e-planning.net,a76893b96338e7e9,RESELLER,c1ba615865ed87b2
|
||||
appnexus.com,15941,RESELLER,f5ab79cb980f11d1
|
||||
video.unrulymedia.com,731539260,RESELLER
|
||||
|
||||
#Rise
|
||||
risecodes.com, 643813aab7212c00011c3f28, DIRECT
|
||||
pubmatic.com, 160295, RESELLER, 5d62403b186f2ace
|
||||
xandr.com, 14082, RESELLER
|
||||
rubiconproject.com, 23876, RESELLER, 0bfd66d529a55807
|
||||
sharethrough.com, 5926d422, RESELLER, d53b998a7bd4ecd2
|
||||
media.net, 8CUQ6928Q, RESELLER
|
||||
sonobi.com, 4a289cdd79, RESELLER, d1a215d9eb5aee9e
|
||||
video.unrulymedia.com, 335119963, RESELLER
|
||||
contextweb.com,562615,RESELLER,89ff185a4c4e857c
|
||||
onetag.com, 69f48c2160c8113, RESELLER
|
||||
33across.com, 0010b00002Xbn7QAAR, RESELLER, bbea06d9c4d2853c
|
||||
yieldmo.com, 2754490424016969782, RESELLER
|
||||
openx.com, 537140488, RESELLER, 6a698e2ec38604c6
|
||||
lijit.com, 405318, RESELLER, fafdf38b16bf6b2b
|
||||
themediagrid.com, 4DQHAP, RESELLER, 35d5010d7789b49d
|
||||
loopme.com, 11362, RESELLER, 6c8d5f95897a5a3b
|
||||
amxrtb.com, 105199691, RESELLER
|
||||
smartadserver.com, 4284, RESELLER
|
||||
adform.com, 3119, RESELLER, 9f5210a2f0999e32
|
||||
smaato.com, 1100057444, RESELLER, 07bcf65f187117b4
|
||||
adyoulike.com, 78afbc34fac571736717317117dfa247, RESELLER
|
||||
|
||||
#Block
|
||||
blockthrough.com, 5130683165442048, DIRECT
|
||||
pubmatic.com, 160377, RESELLER, 5d62403b186f2ace
|
||||
rubiconproject.com, 23718, RESELLER, 0bfd66d529a55807
|
||||
appnexus.com, 6979, RESELLER
|
||||
lijit.com, 251666, RESELLER, fafdf38b16bf6b2b
|
||||
lijit.com, 251666-eb, RESELLER, fafdf38b16bf6b2b
|
||||
video.unrulymedia.com, 2444764291, RESELLER
|
||||
contextweb.com, 558511, RESELLER
|
||||
krushmedia.com, AJxF6R572a9M6CaTvK, RESELLER
|
||||
criteo.com, 8990, RESELLER
|
||||
smartadserver.com, 4485, RESELLER, 060d053dcf45cbf3
|
||||
smartadserver.com, 4485-OB, RESELLER, 060d053dcf45cbf3
|
||||
Contextweb.com, 562926, RESELLER, 89ff185a4c4e857c
|
||||
|
||||
# VT Amazon TAM
|
||||
|
||||
aps.amazon.com,70247b00-ff8f-4016-b3ab-8344daf96e09,DIRECT
|
||||
indexexchange.com, 193067, DIRECT, 50b1c356f2c5c8fc
|
||||
triplelift.com, 6059, DIRECT, 6c33edb13117fd86
|
||||
sharethrough.com, 31c129df, DIRECT, d53b998a7bd4ecd2
|
||||
appnexus.com, 806, DIRECT, f5ab79cb980f11d1
|
||||
risecodes.com, 5fa94677b2db6a00015b22a9, DIRECT
|
||||
minutemedia.com, 01gerz6y43ck, RESELLER
|
||||
themediagrid.com, LTW57M, DIRECT, 35d5010d7789b49d
|
||||
vidazoo.com, 655c85dc63ceeb606a0f365f, DIRECT, b6ada874b4d7d0b2
|
||||
smartadserver.com, 3490, DIRECT
|
||||
|
||||
##################################
|
||||
# AdinPlay.com ads.txt - 2025-04-16
|
||||
##################################
|
||||
|
||||
venatus.com, OFI, DIRECT
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,5 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="512px" height="512px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g><path fill="#9e0000" d="M 57.5,16.5 C 79.6232,16.1252 88.4565,26.7919 84,48.5C 83.2196,51.5117 82.0529,54.3451 80.5,57C 111.436,74.9338 136.936,98.7672 157,128.5C 182.331,109.668 210.831,100.334 242.5,100.5C 242.5,106.833 242.5,113.167 242.5,119.5C 215.494,119.225 191.16,127.058 169.5,143C 181.768,144.418 194.101,145.418 206.5,146C 223.046,163.709 239.379,181.543 255.5,199.5C 271.621,181.543 287.954,163.709 304.5,146C 316.899,145.418 329.232,144.418 341.5,143C 319.84,127.058 295.506,119.225 268.5,119.5C 268.5,113.167 268.5,106.833 268.5,100.5C 300.169,100.334 328.669,109.668 354,128.5C 374.064,98.7672 399.564,74.9338 430.5,57C 422.929,44.1613 423.929,32.1613 433.5,21C 441.053,16.4447 449.053,15.4447 457.5,18C 478.079,25.4111 490.079,39.9111 493.5,61.5C 490.867,80.2657 480.201,87.4324 461.5,83C 458.488,82.2196 455.655,81.0529 453,79.5C 435.066,110.436 411.233,135.936 381.5,156C 400.886,181.491 410.72,210.324 411,242.5C 404.924,243.476 398.758,243.81 392.5,243.5C 391.875,215.457 383.375,190.124 367,167.5C 365.477,180.07 364.477,192.737 364,205.5C 344.658,222.677 325.492,240.01 306.5,257.5C 367.877,327.636 426.377,399.969 482,474.5C 486.572,480.683 490.572,487.016 494,493.5C 465.55,472.718 437.384,451.551 409.5,430C 357.697,387.864 306.197,345.364 255,302.5C 178.278,369.57 98.9443,433.237 17,493.5C 20.4282,487.016 24.4282,480.683 29,474.5C 84.5044,400.043 143.004,327.876 204.5,258C 185.762,240.273 166.596,222.773 147,205.5C 146.523,192.737 145.523,180.07 144,167.5C 127.625,190.124 119.125,215.457 118.5,243.5C 112.242,243.81 106.076,243.476 100,242.5C 100.28,210.324 110.114,181.491 129.5,156C 99.7672,135.936 75.9338,110.436 58,79.5C 46.6724,86.2353 35.5058,86.0686 24.5,79C 18.004,71.1766 16.1706,62.3433 19,52.5C 25.9173,34.08 38.7506,22.08 57.5,16.5 Z"/></g>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 100 100"
|
||||
id="svg10"
|
||||
sodipodi:docname="TraitorIcon.svg"
|
||||
width="100"
|
||||
height="100"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs14" />
|
||||
<sodipodi:namedview
|
||||
id="namedview12"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
height="100px"
|
||||
inkscape:zoom="6.984"
|
||||
inkscape:cx="49.971363"
|
||||
inkscape:cy="62.142039"
|
||||
inkscape:window-width="3072"
|
||||
inkscape:window-height="1653"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="38"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10" />
|
||||
<g
|
||||
transform="translate(0,-952.36218)"
|
||||
id="g4">
|
||||
<path
|
||||
style="color:#000000;text-indent:0;text-transform:none;direction:ltr;baseline-shift:baseline;enable-background:accumulate"
|
||||
d="m 49.682164,957.35229 c -0.08371,0.007 -0.167129,0.0173 -0.249965,0.0312 l -35.995035,7.00077 C 12.072501,964.6449 10.989932,965.96376 11,967.35339 v 32.00351 c 0.0012,0.13594 0.01165,0.27179 0.03125,0.4063 1.29114,9.0104 3.853487,15.5969 8.498827,22.2837 0.990121,1.4292 3.318267,1.6525 4.561871,0.4375 4.250005,-4.1812 7.885056,-7.78 11.967099,-11.9697 0.44046,-0.4164 0.750052,-0.9694 0.87488,-1.5626 0.628358,-3.8527 1.729676,-9.31167 2.530901,-13.59583 h 15.529108 c 1.243283,0.002 2.441795,-0.85453 2.843357,-2.03147 l 9.998621,-29.00319 c 0.567687,-1.6172 -0.568544,-3.6061 -2.249689,-3.93794 l -14.997932,-3.00033 c -0.298144,-0.0562 -0.604826,-0.0667 -0.906125,-0.0312 z m 23.902953,5.00055 c -1.078435,0.15321 -2.042235,0.94196 -2.405918,1.96897 L 58.899643,998.35679 H 44.995311 c -1.406702,-0.005 -2.733797,1.11295 -2.968341,2.50031 l -1.81225,10.9699 -14.27928,13.3139 c -1.128859,1.0576 -1.217322,3.0338 -0.187474,4.188 6.269465,7.139 13.562725,12.4576 22.778108,17.627 0.892103,0.5009 2.044992,0.5009 2.937095,0 19.535202,-10.9581 34.280717,-24.7622 37.494828,-47.1927 0.06997,-10.76177 0.03124,-21.61653 0.03124,-32.40981 0.0049,-1.40704 -1.112648,-2.73446 -2.499655,-2.96908 l -11.99834,-2.00021 c -0.298144,-0.0562 -0.604826,-0.0667 -0.906125,-0.0312 z m -23.621742,1.06261 11.060975,2.18774 -8.186371,23.75262 H 36.996414 c -1.378861,0.004 -2.67845,1.08304 -2.937095,2.43777 l -2.812112,15.06422 c -2.889686,2.9098 -5.919847,5.9695 -8.686302,8.7197 -2.891489,-4.8594 -4.568269,-9.6725 -5.561733,-16.50187 v -29.25322 z m 25.996415,5.28184 7.03028,1.18763 v 29.19071 c -2.868962,19.61817 -15.058331,31.46397 -32.995449,41.78587 -7.128175,-4.1138 -12.790202,-8.2954 -17.716306,-13.4077 l 12.779487,-11.9076 c 0.479911,-0.4525 0.804009,-1.0673 0.906125,-1.719 l 1.59353,-9.4698 h 13.435647 c 1.225447,-0.01 2.403546,-0.8446 2.812112,-2.0002 z"
|
||||
fill="#000000"
|
||||
fill-opacity="1"
|
||||
fill-rule="nonzero"
|
||||
stroke="none"
|
||||
marker="none"
|
||||
visibility="visible"
|
||||
display="inline"
|
||||
overflow="visible"
|
||||
id="path2" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#d40000;fill-opacity:0;stroke-width:0.143184"
|
||||
d="M 21.229102,70.741061 C 20.081432,70.373981 19.32618,69.461642 17.270525,65.959129 14.47136,61.189795 12.54351,55.744636 11.459279,49.545428 L 11.096793,47.472871 V 30.96265 c 0,-13.527886 0.03488,-16.593449 0.193078,-16.970972 0.258937,-0.617909 0.895032,-1.354431 1.391463,-1.611145 0.222726,-0.115176 8.704097,-1.825312 18.847492,-3.8003039 l 18.442537,-3.5908935 7.910402,1.5791153 c 4.350721,0.8685133 8.143647,1.6897975 8.428725,1.8250757 0.961973,0.4564861 1.758687,1.9751714 1.568376,2.9896164 -0.047,0.250541 -2.368442,7.075757 -5.158757,15.167146 -3.44537,9.990924 -5.193245,14.868872 -5.447096,15.201688 -0.205587,0.269539 -0.643902,0.634451 -0.974034,0.810914 -0.596667,0.318933 -0.648799,0.32127 -8.759053,0.392435 l -8.158815,0.07159 -0.470445,2.434135 c -0.258746,1.338774 -0.824052,4.367125 -1.256239,6.729668 -0.432185,2.362543 -0.88633,4.5664 -1.009211,4.897461 -0.177968,0.479472 -1.505767,1.882854 -6.526761,6.898286 -3.466837,3.462998 -6.517904,6.398177 -6.78015,6.52262 -0.601484,0.285423 -1.599068,0.395138 -2.109203,0.231972 z m 5.734389,-11.793889 4.338592,-4.340341 1.438261,-7.722946 c 1.617094,-8.683222 1.602011,-8.634211 2.883301,-9.369051 l 0.745198,-0.427381 8.253581,-0.0013 c 6.783328,-0.0011 8.266754,-0.03567 8.327529,-0.194046 0.04067,-0.105988 1.891336,-5.461638 4.11259,-11.901446 2.549671,-7.391954 3.982955,-11.742017 3.8876,-11.798993 -0.08307,-0.04964 -2.590856,-0.568451 -5.572852,-1.15292 l -5.42181,-1.062727 -16.494063,3.206249 -16.494064,3.206248 v 14.900444 c 0,14.430688 0.0093,14.952195 0.295638,16.541989 0.677724,3.763097 1.813851,7.543992 3.102693,10.32539 0.832524,1.796635 2.062365,4.131162 2.176318,4.131162 0.04559,0 2.035263,-1.953154 4.421488,-4.340342 z"
|
||||
id="path944" />
|
||||
<path
|
||||
style="fill:#d40000;fill-opacity:0;stroke-width:0.143184"
|
||||
d="M 21.229102,70.741061 C 20.081432,70.373981 19.32618,69.461642 17.270525,65.959129 14.47136,61.189795 12.54351,55.744636 11.459279,49.545428 L 11.096793,47.472871 V 30.96265 c 0,-13.527886 0.03488,-16.593449 0.193078,-16.970972 0.258937,-0.617909 0.895032,-1.354431 1.391463,-1.611145 0.222726,-0.115176 8.704097,-1.825312 18.847492,-3.8003039 l 18.442537,-3.5908935 7.910402,1.5791153 c 4.350721,0.8685133 8.143647,1.6897975 8.428725,1.8250757 0.961973,0.4564861 1.758687,1.9751714 1.568376,2.9896164 -0.047,0.250541 -2.368442,7.075757 -5.158757,15.167146 -3.44537,9.990924 -5.193245,14.868872 -5.447096,15.201688 -0.205587,0.269539 -0.643902,0.634451 -0.974034,0.810914 -0.596667,0.318933 -0.648799,0.32127 -8.759053,0.392435 l -8.158815,0.07159 -0.470445,2.434135 c -0.258746,1.338774 -0.824052,4.367125 -1.256239,6.729668 -0.432185,2.362543 -0.88633,4.5664 -1.009211,4.897461 -0.177968,0.479472 -1.505767,1.882854 -6.526761,6.898286 -3.466837,3.462998 -6.517904,6.398177 -6.78015,6.52262 -0.601484,0.285423 -1.599068,0.395138 -2.109203,0.231972 z m 5.734389,-11.793889 4.338592,-4.340341 1.438261,-7.722946 c 1.617094,-8.683222 1.602011,-8.634211 2.883301,-9.369051 l 0.745198,-0.427381 8.253581,-0.0013 c 6.783328,-0.0011 8.266754,-0.03567 8.327529,-0.194046 0.04067,-0.105988 1.891336,-5.461638 4.11259,-11.901446 2.549671,-7.391954 3.982955,-11.742017 3.8876,-11.798993 -0.08307,-0.04964 -2.590856,-0.568451 -5.572852,-1.15292 l -5.42181,-1.062727 -16.494063,3.206249 -16.494064,3.206248 v 14.900444 c 0,14.430688 0.0093,14.952195 0.295638,16.541989 0.677724,3.763097 1.813851,7.543992 3.102693,10.32539 0.832524,1.796635 2.062365,4.131162 2.176318,4.131162 0.04559,0 2.035263,-1.953154 4.421488,-4.340342 z"
|
||||
id="path983" />
|
||||
<path
|
||||
style="fill:#d40000;fill-opacity:0;stroke-width:0.143184"
|
||||
d="M 21.229102,70.741061 C 20.081432,70.373981 19.32618,69.461642 17.270525,65.959129 14.47136,61.189795 12.54351,55.744636 11.459279,49.545428 L 11.096793,47.472871 V 30.96265 c 0,-13.527886 0.03488,-16.593449 0.193078,-16.970972 0.258937,-0.617909 0.895032,-1.354431 1.391463,-1.611145 0.222726,-0.115176 8.704097,-1.825312 18.847492,-3.8003039 l 18.442537,-3.5908935 7.910402,1.5791153 c 4.350721,0.8685133 8.143647,1.6897975 8.428725,1.8250757 0.961973,0.4564861 1.758687,1.9751714 1.568376,2.9896164 -0.047,0.250541 -2.368442,7.075757 -5.158757,15.167146 -3.44537,9.990924 -5.193245,14.868872 -5.447096,15.201688 -0.205587,0.269539 -0.643902,0.634451 -0.974034,0.810914 -0.596667,0.318933 -0.648799,0.32127 -8.759053,0.392435 l -8.158815,0.07159 -0.470445,2.434135 c -0.258746,1.338774 -0.824052,4.367125 -1.256239,6.729668 -0.432185,2.362543 -0.88633,4.5664 -1.009211,4.897461 -0.177968,0.479472 -1.505767,1.882854 -6.526761,6.898286 -3.466837,3.462998 -6.517904,6.398177 -6.78015,6.52262 -0.601484,0.285423 -1.599068,0.395138 -2.109203,0.231972 z m 5.734389,-11.793889 4.338592,-4.340341 1.438261,-7.722946 c 1.617094,-8.683222 1.602011,-8.634211 2.883301,-9.369051 l 0.745198,-0.427381 8.253581,-0.0013 c 6.783328,-0.0011 8.266754,-0.03567 8.327529,-0.194046 0.04067,-0.105988 1.891336,-5.461638 4.11259,-11.901446 2.549671,-7.391954 3.982955,-11.742017 3.8876,-11.798993 -0.08307,-0.04964 -2.590856,-0.568451 -5.572852,-1.15292 l -5.42181,-1.062727 -16.494063,3.206249 -16.494064,3.206248 v 14.900444 c 0,14.430688 0.0093,14.952195 0.295638,16.541989 0.677724,3.763097 1.813851,7.543992 3.102693,10.32539 0.832524,1.796635 2.062365,4.131162 2.176318,4.131162 0.04559,0 2.035263,-1.953154 4.421488,-4.340342 z"
|
||||
id="path1022" />
|
||||
<path
|
||||
style="fill:#d40000;fill-opacity:0;stroke-width:0.143184"
|
||||
d="M 21.229102,70.741061 C 20.081432,70.373981 19.32618,69.461642 17.270525,65.959129 14.47136,61.189795 12.54351,55.744636 11.459279,49.545428 L 11.096793,47.472871 V 30.96265 c 0,-13.527886 0.03488,-16.593449 0.193078,-16.970972 0.258937,-0.617909 0.895032,-1.354431 1.391463,-1.611145 0.222726,-0.115176 8.704097,-1.825312 18.847492,-3.8003039 l 18.442537,-3.5908935 7.910402,1.5791153 c 4.350721,0.8685133 8.143647,1.6897975 8.428725,1.8250757 0.961973,0.4564861 1.758687,1.9751714 1.568376,2.9896164 -0.047,0.250541 -2.368442,7.075757 -5.158757,15.167146 -3.44537,9.990924 -5.193245,14.868872 -5.447096,15.201688 -0.205587,0.269539 -0.643902,0.634451 -0.974034,0.810914 -0.596667,0.318933 -0.648799,0.32127 -8.759053,0.392435 l -8.158815,0.07159 -0.470445,2.434135 c -0.258746,1.338774 -0.824052,4.367125 -1.256239,6.729668 -0.432185,2.362543 -0.88633,4.5664 -1.009211,4.897461 -0.177968,0.479472 -1.505767,1.882854 -6.526761,6.898286 -3.466837,3.462998 -6.517904,6.398177 -6.78015,6.52262 -0.601484,0.285423 -1.599068,0.395138 -2.109203,0.231972 z m 5.734389,-11.793889 4.338592,-4.340341 1.438261,-7.722946 c 1.617094,-8.683222 1.602011,-8.634211 2.883301,-9.369051 l 0.745198,-0.427381 8.253581,-0.0013 c 6.783328,-0.0011 8.266754,-0.03567 8.327529,-0.194046 0.04067,-0.105988 1.891336,-5.461638 4.11259,-11.901446 2.549671,-7.391954 3.982955,-11.742017 3.8876,-11.798993 -0.08307,-0.04964 -2.590856,-0.568451 -5.572852,-1.15292 l -5.42181,-1.062727 -16.494063,3.206249 -16.494064,3.206248 v 14.900444 c 0,14.430688 0.0093,14.952195 0.295638,16.541989 0.677724,3.763097 1.813851,7.543992 3.102693,10.32539 0.832524,1.796635 2.062365,4.131162 2.176318,4.131162 0.04559,0 2.035263,-1.953154 4.421488,-4.340342 z"
|
||||
id="path1061" />
|
||||
<path
|
||||
style="fill:#d40000;fill-opacity:0;stroke-width:0.143184"
|
||||
d="M 21.229102,70.741061 C 20.081432,70.373981 19.32618,69.461642 17.270525,65.959129 14.47136,61.189795 12.54351,55.744636 11.459279,49.545428 L 11.096793,47.472871 V 30.96265 c 0,-13.527886 0.03488,-16.593449 0.193078,-16.970972 0.258937,-0.617909 0.895032,-1.354431 1.391463,-1.611145 0.222726,-0.115176 8.704097,-1.825312 18.847492,-3.8003039 l 18.442537,-3.5908935 7.910402,1.5791153 c 4.350721,0.8685133 8.143647,1.6897975 8.428725,1.8250757 0.961973,0.4564861 1.758687,1.9751714 1.568376,2.9896164 -0.047,0.250541 -2.368442,7.075757 -5.158757,15.167146 -3.44537,9.990924 -5.193245,14.868872 -5.447096,15.201688 -0.205587,0.269539 -0.643902,0.634451 -0.974034,0.810914 -0.596667,0.318933 -0.648799,0.32127 -8.759053,0.392435 l -8.158815,0.07159 -0.470445,2.434135 c -0.258746,1.338774 -0.824052,4.367125 -1.256239,6.729668 -0.432185,2.362543 -0.88633,4.5664 -1.009211,4.897461 -0.177968,0.479472 -1.505767,1.882854 -6.526761,6.898286 -3.466837,3.462998 -6.517904,6.398177 -6.78015,6.52262 -0.601484,0.285423 -1.599068,0.395138 -2.109203,0.231972 z m 5.734389,-11.793889 4.338592,-4.340341 1.438261,-7.722946 c 1.617094,-8.683222 1.602011,-8.634211 2.883301,-9.369051 l 0.745198,-0.427381 8.253581,-0.0013 c 6.783328,-0.0011 8.266754,-0.03567 8.327529,-0.194046 0.04067,-0.105988 1.891336,-5.461638 4.11259,-11.901446 2.549671,-7.391954 3.982955,-11.742017 3.8876,-11.798993 -0.08307,-0.04964 -2.590856,-0.568451 -5.572852,-1.15292 l -5.42181,-1.062727 -16.494063,3.206249 -16.494064,3.206248 v 14.900444 c 0,14.430688 0.0093,14.952195 0.295638,16.541989 0.677724,3.763097 1.813851,7.543992 3.102693,10.32539 0.832524,1.796635 2.062365,4.131162 2.176318,4.131162 0.04559,0 2.035263,-1.953154 4.421488,-4.340342 z"
|
||||
id="path1100" />
|
||||
<path
|
||||
style="fill:#d40000;fill-opacity:0;stroke-width:0.143184"
|
||||
d="M 22.451679,62.831985 C 22.040716,62.318926 20.614744,59.414324 19.904415,57.643392 18.969118,55.311589 18.276544,53.003918 17.644871,50.114548 L 17.190978,48.038373 17.14702,32.753436 c -0.02418,-8.406715 0.006,-15.283838 0.06717,-15.282495 0.06112,0.0013 7.450905,-1.423883 16.421746,-3.16717 l 16.310618,-3.169613 5.310229,1.037928 c 2.920626,0.570861 5.383063,1.077918 5.472081,1.126793 0.108366,0.0595 -1.175715,3.968788 -3.885785,11.829987 l -4.047636,11.741123 -8.356483,0.07159 c -6.695958,0.05736 -8.427615,0.110521 -8.714445,0.267501 -0.60381,0.330461 -1.419391,1.193902 -1.576025,1.66851 -0.08176,0.247726 -0.783479,3.851808 -1.559383,8.00907 l -1.410734,7.55866 -4.287731,4.287731 c -3.320136,3.320136 -4.321858,4.245124 -4.438961,4.098932 z"
|
||||
id="path1139" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.143184"
|
||||
d="m 40.914948,36.977471 c 2.185353,-0.02223 5.761384,-0.02223 7.946736,0 2.185352,0.02223 0.397337,0.04042 -3.973368,0.04042 -4.370704,0 -6.15872,-0.01819 -3.973368,-0.04042 z"
|
||||
id="path1178" />
|
||||
<path
|
||||
style="fill:#aa0000;fill-opacity:1;stroke-width:0.143184"
|
||||
d="M 21.229102,70.741061 C 20.081432,70.373981 19.32618,69.461642 17.270525,65.959129 14.47136,61.189795 12.54351,55.744636 11.459279,49.545428 L 11.096793,47.472871 V 30.96265 c 0,-13.527886 0.03488,-16.593449 0.193078,-16.970972 0.258937,-0.617909 0.895032,-1.354431 1.391463,-1.611145 0.222726,-0.115176 8.704097,-1.825312 18.847492,-3.8003039 l 18.442537,-3.5908935 7.910402,1.5791153 c 4.350721,0.8685133 8.143647,1.6897975 8.428725,1.8250757 0.961973,0.4564861 1.758687,1.9751714 1.568376,2.9896164 -0.047,0.250541 -2.368442,7.075757 -5.158757,15.167146 -3.44537,9.990924 -5.193245,14.868872 -5.447096,15.201688 -0.205587,0.269539 -0.643902,0.634451 -0.974034,0.810914 -0.596667,0.318933 -0.648799,0.32127 -8.759053,0.392435 l -8.158815,0.07159 -0.470445,2.434135 c -0.258746,1.338774 -0.824052,4.367125 -1.256239,6.729668 -0.432185,2.362543 -0.88633,4.5664 -1.009211,4.897461 -0.177968,0.479472 -1.505767,1.882854 -6.526761,6.898286 -3.466837,3.462998 -6.517904,6.398177 -6.78015,6.52262 -0.601484,0.285423 -1.599068,0.395138 -2.109203,0.231972 z m 5.734389,-11.793889 4.338592,-4.340341 1.438261,-7.722946 c 1.617094,-8.683222 1.602011,-8.634211 2.883301,-9.369051 l 0.745198,-0.427381 8.253581,-0.0013 c 6.783328,-0.0011 8.266754,-0.03567 8.327529,-0.194046 0.04067,-0.105988 1.891336,-5.461638 4.11259,-11.901446 2.549671,-7.391954 3.982955,-11.742017 3.8876,-11.798993 -0.08307,-0.04964 -2.590856,-0.568451 -5.572852,-1.15292 l -5.42181,-1.062727 -16.494063,3.206249 -16.494064,3.206248 v 14.900444 c 0,14.430688 0.0093,14.952195 0.295638,16.541989 0.677724,3.763097 1.813851,7.543992 3.102693,10.32539 0.832524,1.796635 2.062365,4.131162 2.176318,4.131162 0.04559,0 2.035263,-1.953154 4.421488,-4.340342 z"
|
||||
id="path9953" />
|
||||
<path
|
||||
style="fill:#aa0000;fill-opacity:1;stroke-width:0.143184"
|
||||
d="M 49.327033,94.804719 C 48.714427,94.60513 47.768416,94.086861 45.174685,92.529868 37.917796,88.173624 32.73106,84.14232 27.795635,79.022291 c -1.145161,-1.187994 -2.213316,-2.431436 -2.374239,-2.763859 -0.397355,-0.820826 -0.385516,-1.87334 0.02963,-2.634656 0.211883,-0.388557 2.674852,-2.777493 7.195017,-6.978744 3.780069,-3.513371 7.046744,-6.559837 7.25928,-6.769926 0.367228,-0.363001 0.433179,-0.666405 1.327443,-6.106826 1.040786,-6.331825 1.066013,-6.412508 2.250084,-7.196324 l 0.596675,-0.39498 7.446948,-0.07159 7.446947,-0.07159 6.097509,-16.895762 c 3.353629,-9.292669 6.184278,-17.104473 6.290327,-17.359565 0.271361,-0.652727 1.177566,-1.423686 1.905252,-1.620901 0.55181,-0.149551 1.267997,-0.05876 6.811404,0.863504 3.407116,0.566845 6.423919,1.093902 6.704006,1.171238 0.637026,0.175891 1.521753,0.963097 1.870312,1.664151 0.253992,0.510853 0.264529,1.240952 0.247407,17.14297 -0.01766,16.399934 -0.02182,16.631061 -0.329684,18.327606 -2.58954,14.26993 -9.981308,25.824745 -22.991493,35.940307 -3.466755,2.695441 -7.896512,5.630746 -12.608706,8.354951 -2.064486,1.193517 -2.838015,1.444605 -3.642726,1.182428 z M 52.47709,87.119888 C 59.271254,83.059324 64.365846,79.189404 68.951193,74.605944 76.565106,66.995162 80.844811,58.899716 82.755326,48.494191 l 0.363231,-1.978318 V 32.007709 17.499544 l -0.322165,-0.07398 c -0.957493,-0.219871 -6.829216,-1.157603 -6.884521,-1.099478 -0.03586,0.03769 -2.785127,7.60719 -6.109478,16.821108 -3.324352,9.213917 -6.177636,16.993316 -6.340633,17.287551 -0.353145,0.637482 -1.164147,1.232386 -1.918855,1.407559 -0.300488,0.06975 -3.5908,0.128346 -7.311805,0.130225 -3.721005,0.0019 -6.765464,0.04073 -6.765464,0.08633 0,0.474978 -1.637454,9.59135 -1.804511,10.046442 -0.187845,0.511725 -1.260378,1.573508 -6.797799,6.729668 -3.61612,3.367143 -6.574871,6.194451 -6.575002,6.282908 -3e-4,0.204934 2.512831,2.628316 4.451717,4.292733 1.895466,1.627144 5.431341,4.277542 7.645781,5.731075 2.084726,1.368391 5.558468,3.470987 5.657133,3.424166 0.03938,-0.01869 1.134737,-0.669367 2.434135,-1.44596 z"
|
||||
id="path10066" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 17 KiB |
@@ -120,7 +120,8 @@
|
||||
"pangaea": "Pangaea",
|
||||
"japan": "Japan and Neighbors",
|
||||
"betweentwoseas": "Between Two Seas",
|
||||
"knownworld": "Known World"
|
||||
"knownworld": "Known World",
|
||||
"faroeislands": "Faroe Islands"
|
||||
},
|
||||
"private_lobby": {
|
||||
"title": "Join Private Lobby",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "Faroe Islands",
|
||||
"width": 1600,
|
||||
"height": 2000,
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [920, 1780],
|
||||
"name": "Suduroy Region",
|
||||
"strength": 2,
|
||||
"flag": "fo"
|
||||
},
|
||||
{
|
||||
"coordinates": [880, 1070],
|
||||
"name": "Sandoy Region",
|
||||
"strength": 2,
|
||||
"flag": "fo"
|
||||
},
|
||||
{
|
||||
"coordinates": [480, 630],
|
||||
"name": "Vagar Region",
|
||||
"strength": 1,
|
||||
"flag": "fo"
|
||||
},
|
||||
{
|
||||
"coordinates": [735, 580],
|
||||
"name": "Streymoy Region",
|
||||
"strength": 2,
|
||||
"flag": "fo"
|
||||
},
|
||||
{
|
||||
"coordinates": [815, 375],
|
||||
"name": "Eysturoy Region",
|
||||
"strength": 2,
|
||||
"flag": "fo"
|
||||
},
|
||||
{
|
||||
"coordinates": [1115, 265],
|
||||
"name": "Nordoyar Region",
|
||||
"strength": 2,
|
||||
"flag": "fo"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 663 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
@@ -1,37 +1,80 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive setup script for Hetzner server with Docker and Cloudflare R2 configuration
|
||||
|
||||
# Comprehensive setup script for Hetzner server with Docker and user setup
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
echo "====================================================="
|
||||
echo "🚀 STARTING SERVER SETUP"
|
||||
echo "====================================================="
|
||||
|
||||
echo "🔄 Updating system..."
|
||||
apt update && apt upgrade -y
|
||||
|
||||
echo "🐳 Installing Docker..."
|
||||
# Install Docker using official script
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sh get-docker.sh
|
||||
systemctl enable --now docker
|
||||
# Check if Docker is already installed
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "Docker is already installed"
|
||||
else
|
||||
echo "🐳 Installing Docker..."
|
||||
# Install Docker using official script
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sh get-docker.sh
|
||||
systemctl enable --now docker
|
||||
echo "Docker installed successfully"
|
||||
fi
|
||||
|
||||
# Set up Docker Hub credentials
|
||||
echo "🔐 Setting up Docker Hub login..."
|
||||
echo "Enter your Docker Hub username:"
|
||||
read DOCKER_USERNAME
|
||||
echo "Enter your Docker Hub password/token:"
|
||||
read -s DOCKER_PASSWORD
|
||||
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
|
||||
echo "✅ Docker Hub login configured"
|
||||
echo "👤 Setting up openfront user..."
|
||||
# Create openfront user if it doesn't exist
|
||||
if id "openfront" &>/dev/null; then
|
||||
echo "User openfront already exists"
|
||||
else
|
||||
useradd -m -s /bin/bash openfront
|
||||
echo "User openfront created"
|
||||
fi
|
||||
|
||||
echo "🔄 Installing Node Exporter..."
|
||||
# Check if openfront is already in docker group
|
||||
if groups openfront | grep -q '\bdocker\b'; then
|
||||
echo "User openfront is already in the docker group"
|
||||
else
|
||||
# Add openfront to docker group
|
||||
usermod -aG docker openfront
|
||||
echo "Added openfront to docker group"
|
||||
fi
|
||||
|
||||
docker run -d --name node-exporter --restart=unless-stopped \
|
||||
--net="host" \
|
||||
--pid="host" \
|
||||
-v "/:/host:ro,rslave" \
|
||||
prom/node-exporter:latest \
|
||||
--path.rootfs=/host
|
||||
# Create .ssh directory for openfront if it doesn't exist
|
||||
if [ ! -d "/home/openfront/.ssh" ]; then
|
||||
mkdir -p /home/openfront/.ssh
|
||||
chmod 700 /home/openfront/.ssh
|
||||
echo "Created .ssh directory for openfront"
|
||||
fi
|
||||
|
||||
echo "node-exporter installed"
|
||||
# Copy SSH keys from root if they exist and haven't been copied yet
|
||||
if [ -f /root/.ssh/authorized_keys ] && [ ! -f /home/openfront/.ssh/authorized_keys ]; then
|
||||
cp /root/.ssh/authorized_keys /home/openfront/.ssh/
|
||||
chmod 600 /home/openfront/.ssh/authorized_keys
|
||||
echo "SSH keys copied from root to openfront"
|
||||
fi
|
||||
|
||||
echo "🎉 Setup complete! You can find helpful Docker and R2 commands in ~/docker-commands.sh"
|
||||
echo "Test your R2 connection: aws s3 ls --profile r2"
|
||||
# Check if node-exporter container already exists
|
||||
if docker ps -a | grep -q "node-exporter"; then
|
||||
echo "Node Exporter is already installed"
|
||||
else
|
||||
echo "🔄 Installing Node Exporter..."
|
||||
docker run -d --name node-exporter --restart=unless-stopped \
|
||||
--net="host" \
|
||||
--pid="host" \
|
||||
-v "/:/host:ro,rslave" \
|
||||
prom/node-exporter:latest \
|
||||
--path.rootfs=/host
|
||||
echo "Node Exporter installed successfully"
|
||||
fi
|
||||
|
||||
# Set proper ownership for openfront's home directory
|
||||
chown -R openfront:openfront /home/openfront
|
||||
echo "Set proper ownership for openfront's home directory"
|
||||
|
||||
echo "====================================================="
|
||||
echo "🎉 SETUP COMPLETE!"
|
||||
echo "====================================================="
|
||||
echo "The openfront user has been set up and has Docker permissions."
|
||||
echo "You can now deploy using the openfront user."
|
||||
echo "====================================================="
|
||||
@@ -12,6 +12,7 @@ import { createGameRecord } from "../core/Util";
|
||||
import { ServerConfig } from "../core/configuration/Config";
|
||||
import { getConfig } from "../core/configuration/ConfigLoader";
|
||||
import { Team, UnitType } from "../core/game/Game";
|
||||
import { TileRef } from "../core/game/GameMap";
|
||||
import {
|
||||
ErrorUpdate,
|
||||
GameUpdateType,
|
||||
@@ -28,6 +29,7 @@ import { endGame, startGame, startTime } from "./LocalPersistantStats";
|
||||
import { getPersistentIDFromCookie } from "./Main";
|
||||
import {
|
||||
SendAttackIntentEvent,
|
||||
SendBoatAttackIntentEvent,
|
||||
SendHashEvent,
|
||||
SendSpawnIntentEvent,
|
||||
Transport,
|
||||
@@ -359,6 +361,18 @@ export class ClientGameRunner {
|
||||
this.myPlayer.troops() * this.renderer.uiState.attackRatio,
|
||||
),
|
||||
);
|
||||
} else if (
|
||||
actions.canBoat !== false &&
|
||||
this.shouldBoat(tile, actions.canBoat) &&
|
||||
this.gameView.isLand(tile)
|
||||
) {
|
||||
this.eventBus.emit(
|
||||
new SendBoatAttackIntentEvent(
|
||||
this.gameView.owner(tile).id(),
|
||||
cell,
|
||||
this.myPlayer.troops() * this.renderer.uiState.attackRatio,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const owner = this.gameView.owner(tile);
|
||||
@@ -370,6 +384,18 @@ export class ClientGameRunner {
|
||||
});
|
||||
}
|
||||
|
||||
private shouldBoat(tile: TileRef, src: TileRef) {
|
||||
// TODO: Global enable flag
|
||||
// TODO: Global limit autoboat to nearby shore flag
|
||||
// if (!enableAutoBoat) return false;
|
||||
// if (!limitAutoBoatNear) return true;
|
||||
const distanceSquared = this.gameView.euclideanDistSquared(tile, src);
|
||||
const limit = 100;
|
||||
const limitSquared = limit * limit;
|
||||
if (distanceSquared > limitSquared) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private onMouseMove(event: MouseMoveEvent) {
|
||||
this.lastMousePosition = { x: event.x, y: event.y };
|
||||
this.checkTileUnderCursor();
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
export class MultiTabDetector {
|
||||
private focusChanges: number[] = [];
|
||||
private readonly maxFocusChanges: number = 10;
|
||||
private readonly timeWindow: number = 60_000;
|
||||
private readonly punishmentDelays: number[] = [
|
||||
2_000, 3_000, 5_000, 10_000, 30_000, 60_000,
|
||||
];
|
||||
private lastFocusChangeTime: number = 0;
|
||||
private isPunished: boolean = false;
|
||||
private isMonitoring: boolean = false;
|
||||
private startPenaltyCallback?: (duration: number) => void;
|
||||
|
||||
private numPunishmentsGiven = 0;
|
||||
|
||||
/**
|
||||
* Start monitoring for multi-tabbing behavior
|
||||
*
|
||||
* @param startPenalty Callback function when punishment starts
|
||||
*/
|
||||
public startMonitoring(startPenalty: (duration: number) => void): void {
|
||||
if (this.isMonitoring) return;
|
||||
|
||||
this.isMonitoring = true;
|
||||
this.startPenaltyCallback = startPenalty;
|
||||
|
||||
// Event listeners for window focus/blur
|
||||
window.addEventListener("blur", this.handleFocusChange.bind(this));
|
||||
window.addEventListener("focus", this.handleFocusChange.bind(this));
|
||||
|
||||
// Also track visibility changes for tab switching
|
||||
document.addEventListener(
|
||||
"visibilitychange",
|
||||
this.handleVisibilityChange.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
public stopMonitoring(): void {
|
||||
if (!this.isMonitoring) return;
|
||||
|
||||
this.isMonitoring = false;
|
||||
|
||||
// Remove event listeners
|
||||
window.removeEventListener("blur", this.handleFocusChange.bind(this));
|
||||
window.removeEventListener("focus", this.handleFocusChange.bind(this));
|
||||
document.removeEventListener(
|
||||
"visibilitychange",
|
||||
this.handleVisibilityChange.bind(this),
|
||||
);
|
||||
|
||||
// Clear data
|
||||
this.focusChanges = [];
|
||||
this.isPunished = false;
|
||||
}
|
||||
|
||||
private handleFocusChange(): void {
|
||||
const currentTime = Date.now();
|
||||
|
||||
this.recordFocusChange(currentTime);
|
||||
|
||||
// Check for multi-tabbing when focus is gained
|
||||
if (document.hasFocus() && !this.isPunished) {
|
||||
this.checkForMultiTabbing(currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
private handleVisibilityChange(): void {
|
||||
const currentTime = Date.now();
|
||||
|
||||
// Record and check regardless of current focus state
|
||||
this.recordFocusChange(currentTime);
|
||||
|
||||
// Only check when tab becomes visible
|
||||
if (document.visibilityState === "visible" && !this.isPunished) {
|
||||
this.checkForMultiTabbing(currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
private recordFocusChange(timestamp: number): void {
|
||||
if (Math.abs(this.lastFocusChangeTime - timestamp) < 100) {
|
||||
// Don't count multiple triggers at same time
|
||||
return;
|
||||
}
|
||||
this.focusChanges.push(timestamp);
|
||||
console.log(`pushing focus change at ${timestamp}`);
|
||||
this.lastFocusChangeTime = timestamp;
|
||||
|
||||
// Keep only recent changes
|
||||
if (this.focusChanges.length > this.maxFocusChanges) {
|
||||
this.focusChanges.shift();
|
||||
}
|
||||
}
|
||||
|
||||
private checkForMultiTabbing(currentTime: number): void {
|
||||
// Only if we have enough data points
|
||||
if (this.focusChanges.length >= this.maxFocusChanges) {
|
||||
const oldestChange = this.focusChanges[0];
|
||||
const timeSpan = currentTime - oldestChange;
|
||||
|
||||
// If changes happened within detection window
|
||||
if (timeSpan <= this.timeWindow) {
|
||||
this.applyPunishment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private applyPunishment(): void {
|
||||
// Prevent multiple punishments
|
||||
if (this.isPunished) return;
|
||||
this.isPunished = true;
|
||||
|
||||
let punishmentDelay = 0;
|
||||
if (this.numPunishmentsGiven >= this.punishmentDelays.length) {
|
||||
punishmentDelay = this.punishmentDelays[this.punishmentDelays.length - 1];
|
||||
} else {
|
||||
punishmentDelay = this.punishmentDelays[this.numPunishmentsGiven];
|
||||
}
|
||||
|
||||
this.numPunishmentsGiven++;
|
||||
|
||||
// Call the start penalty callback
|
||||
if (this.startPenaltyCallback) {
|
||||
this.startPenaltyCallback(punishmentDelay);
|
||||
}
|
||||
|
||||
// Remove penalty after delay
|
||||
setTimeout(() => {
|
||||
this.isPunished = false;
|
||||
}, punishmentDelay);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ export const MapDescription: Record<keyof typeof GameMapType, string> = {
|
||||
Japan: "Japan",
|
||||
BetweenTwoSeas: "Between Two Seas",
|
||||
KnownWorld: "Known World",
|
||||
FaroeIslands: "Faroe Islands",
|
||||
};
|
||||
|
||||
@customElement("map-display")
|
||||
|
||||
@@ -12,6 +12,7 @@ import { EmojiTable } from "./layers/EmojiTable";
|
||||
import { EventsDisplay } from "./layers/EventsDisplay";
|
||||
import { Layer } from "./layers/Layer";
|
||||
import { Leaderboard } from "./layers/Leaderboard";
|
||||
import { MultiTabModal } from "./layers/MultiTabModal";
|
||||
import { NameLayer } from "./layers/NameLayer";
|
||||
import { OptionsMenu } from "./layers/OptionsMenu";
|
||||
import { PlayerInfoOverlay } from "./layers/PlayerInfoOverlay";
|
||||
@@ -125,6 +126,14 @@ export function createRenderer(
|
||||
playerPanel.eventBus = eventBus;
|
||||
playerPanel.emojiTable = emojiTable;
|
||||
|
||||
const multiTabModal = document.querySelector(
|
||||
"multi-tab-modal",
|
||||
) as MultiTabModal;
|
||||
if (!(multiTabModal instanceof MultiTabModal)) {
|
||||
console.error("multi-tab modal not found");
|
||||
}
|
||||
multiTabModal.game = game;
|
||||
|
||||
const layers: Layer[] = [
|
||||
new TerrainLayer(game, transformHandler),
|
||||
new TerritoryLayer(game, eventBus),
|
||||
@@ -153,6 +162,7 @@ export function createRenderer(
|
||||
optionsMenu,
|
||||
topBar,
|
||||
playerPanel,
|
||||
multiTabModal,
|
||||
];
|
||||
|
||||
return new GameRenderer(
|
||||
|
||||
@@ -76,10 +76,11 @@ export const getColoredSprite = (
|
||||
unit: UnitView,
|
||||
theme: Theme,
|
||||
customTerritoryColor?: Colord,
|
||||
customBorderColor?: Colord,
|
||||
): HTMLCanvasElement => {
|
||||
const owner = unit.owner();
|
||||
const territoryColor = customTerritoryColor ?? theme.territoryColor(owner);
|
||||
const borderColor = theme.borderColor(owner);
|
||||
const borderColor = customBorderColor ?? theme.borderColor(owner);
|
||||
const spawnHighlightColor = theme.spawnHighlightColor();
|
||||
const colorKey = customTerritoryColor
|
||||
? customTerritoryColor.toRgbString()
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { GameType } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { MultiTabDetector } from "../../MultiTabDetector";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("multi-tab-modal")
|
||||
export class MultiTabModal extends LitElement implements Layer {
|
||||
public game: GameView;
|
||||
|
||||
private detector: MultiTabDetector;
|
||||
|
||||
@property({ type: Number }) duration: number = 5000;
|
||||
@state() private countdown: number = 5;
|
||||
@state() private isVisible: boolean = false;
|
||||
|
||||
private intervalId?: number;
|
||||
|
||||
// Disable shadow DOM to allow Tailwind classes to work
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (
|
||||
this.game.inSpawnPhase() ||
|
||||
this.game.config().gameConfig().gameType == GameType.Singleplayer
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!this.detector) {
|
||||
this.detector = new MultiTabDetector();
|
||||
this.detector.startMonitoring((duration: number) => {
|
||||
this.show(duration);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Show the modal with penalty information
|
||||
public show(duration: number): void {
|
||||
if (!this.game.myPlayer()?.isAlive()) {
|
||||
return;
|
||||
}
|
||||
this.duration = duration;
|
||||
this.countdown = Math.ceil(duration / 1000);
|
||||
this.isVisible = true;
|
||||
|
||||
// Start countdown timer
|
||||
this.intervalId = window.setInterval(() => {
|
||||
this.countdown--;
|
||||
|
||||
if (this.countdown <= 0) {
|
||||
this.hide();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
// Hide the modal
|
||||
public hide(): void {
|
||||
this.isVisible = false;
|
||||
|
||||
if (this.intervalId) {
|
||||
window.clearInterval(this.intervalId);
|
||||
this.intervalId = undefined;
|
||||
}
|
||||
|
||||
// Dispatch event when modal is closed
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("penalty-complete", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this.intervalId) {
|
||||
window.clearInterval(this.intervalId);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.isVisible) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="fixed inset-0 z-50 overflow-auto bg-red-500/20 flex items-center justify-center"
|
||||
>
|
||||
<div
|
||||
class="relative p-6 bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-md w-full m-4 transition-all transform"
|
||||
>
|
||||
<h2 class="text-2xl font-bold mb-4 text-red-600 dark:text-red-400">
|
||||
${translateText("multi_tab.warning")}
|
||||
</h2>
|
||||
|
||||
<p class="mb-4 text-gray-800 dark:text-gray-200">
|
||||
${translateText("multi_tab.detected")}
|
||||
</p>
|
||||
|
||||
<p class="mb-4 text-gray-800 dark:text-gray-200">
|
||||
${translateText("multi_tab.please_wait")}
|
||||
<span class="font-bold text-xl">${this.countdown}</span>
|
||||
${translateText("multi_tab.seconds")}
|
||||
</p>
|
||||
|
||||
<div
|
||||
class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5 mb-4"
|
||||
>
|
||||
<div
|
||||
class="bg-red-600 dark:bg-red-500 h-2.5 rounded-full transition-all duration-1000 ease-linear"
|
||||
style="width: ${(this.countdown / (this.duration / 1000)) * 100}%"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
${translateText("multi_tab.explanation")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -145,8 +145,14 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
this.eventBus.on(MouseUpEvent, (e: MouseEvent) => this.hide());
|
||||
}
|
||||
|
||||
tick() {
|
||||
this.requestUpdate();
|
||||
async tick() {
|
||||
if (this.isVisible && this.tile) {
|
||||
const myPlayer = this.g.myPlayer();
|
||||
if (myPlayer !== null && myPlayer.isAlive()) {
|
||||
this.actions = await myPlayer.actions(this.tile);
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTotalNukesSent(otherId: PlayerID): number {
|
||||
|
||||
@@ -8,7 +8,7 @@ import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
||||
import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg";
|
||||
import { consolex } from "../../../core/Consolex";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Cell, PlayerActions } from "../../../core/game/Game";
|
||||
import { Cell, PlayerActions, TerraNullius } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
@@ -44,6 +44,7 @@ export class RadialMenu implements Layer {
|
||||
private clickedCell: Cell | null = null;
|
||||
private lastClosed: number = 0;
|
||||
|
||||
private originalTileOwner: PlayerView | TerraNullius;
|
||||
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined>;
|
||||
private isVisible: boolean = false;
|
||||
private readonly menuItems = new Map([
|
||||
@@ -267,8 +268,26 @@ export class RadialMenu implements Layer {
|
||||
.style("pointer-events", "none");
|
||||
}
|
||||
|
||||
tick() {
|
||||
// Update logic if needed
|
||||
async tick() {
|
||||
// Only update when menu is visible
|
||||
if (!this.isVisible || this.clickedCell === null) return;
|
||||
const myPlayer = this.g.myPlayer();
|
||||
if (myPlayer === null || !myPlayer.isAlive()) return;
|
||||
const tile = this.g.ref(this.clickedCell.x, this.clickedCell.y);
|
||||
if (this.originalTileOwner.isPlayer()) {
|
||||
if (this.g.owner(tile) != this.originalTileOwner) {
|
||||
this.closeMenu();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (this.g.owner(tile).isPlayer() || this.g.owner(tile) == myPlayer) {
|
||||
this.closeMenu();
|
||||
return;
|
||||
}
|
||||
}
|
||||
const actions = await myPlayer.actions(tile);
|
||||
this.disableAllButtons();
|
||||
this.handlePlayerActions(myPlayer, actions, tile);
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
@@ -291,12 +310,7 @@ export class RadialMenu implements Layer {
|
||||
} else {
|
||||
this.showRadialMenu(event.x, event.y);
|
||||
}
|
||||
this.enableCenterButton(false);
|
||||
for (const item of this.menuItems.values()) {
|
||||
item.disabled = true;
|
||||
this.updateMenuItemState(item);
|
||||
}
|
||||
|
||||
this.disableAllButtons();
|
||||
this.clickedCell = this.transformHandler.screenToWorldCoordinates(
|
||||
event.x,
|
||||
event.y,
|
||||
@@ -305,7 +319,7 @@ export class RadialMenu implements Layer {
|
||||
return;
|
||||
}
|
||||
const tile = this.g.ref(this.clickedCell.x, this.clickedCell.y);
|
||||
|
||||
this.originalTileOwner = this.g.owner(tile);
|
||||
if (this.g.inSpawnPhase()) {
|
||||
if (this.g.isLand(tile) && !this.g.hasOwner(tile)) {
|
||||
this.enableCenterButton(true);
|
||||
@@ -313,10 +327,8 @@ export class RadialMenu implements Layer {
|
||||
return;
|
||||
}
|
||||
|
||||
const myPlayer = this.g
|
||||
.playerViews()
|
||||
.find((p) => p.clientID() == this.clientID);
|
||||
if (!myPlayer) {
|
||||
const myPlayer = this.g.myPlayer();
|
||||
if (myPlayer === null) {
|
||||
consolex.warn("my player not found");
|
||||
return;
|
||||
}
|
||||
@@ -430,6 +442,14 @@ export class RadialMenu implements Layer {
|
||||
this.hideRadialMenu();
|
||||
}
|
||||
|
||||
private disableAllButtons() {
|
||||
this.enableCenterButton(false);
|
||||
for (const item of this.menuItems.values()) {
|
||||
item.disabled = true;
|
||||
this.updateMenuItemState(item);
|
||||
}
|
||||
}
|
||||
|
||||
private activateMenuElement(
|
||||
slot: Slot,
|
||||
color: string,
|
||||
|
||||
@@ -203,6 +203,18 @@ export class UnitLayer implements Layer {
|
||||
?.[GameUpdateType.Unit]?.forEach((unit) => {
|
||||
this.onUnitEvent(this.game.unit(unit.id));
|
||||
});
|
||||
this.boatToTrail.forEach((trail, unit) => {
|
||||
for (const t of trail) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
this.relationship(unit),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
150,
|
||||
this.transportShipTrailContext,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private relationship(unit: UnitView): Relationship {
|
||||
@@ -365,8 +377,6 @@ export class UnitLayer implements Layer {
|
||||
}
|
||||
|
||||
private handleTradeShipEvent(unit: UnitView) {
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
// Clear previous area
|
||||
for (const t of this.game.bfs(
|
||||
unit.lastTile(),
|
||||
@@ -479,7 +489,29 @@ export class UnitLayer implements Layer {
|
||||
const x = this.game.x(unit.tile());
|
||||
const y = this.game.y(unit.tile());
|
||||
|
||||
const sprite = getColoredSprite(unit, this.theme, customTerritoryColor);
|
||||
let alternateViewColor = null;
|
||||
|
||||
if (this.alternateView) {
|
||||
const rel = this.relationship(unit);
|
||||
switch (rel) {
|
||||
case Relationship.Self:
|
||||
alternateViewColor = this.theme.selfColor();
|
||||
break;
|
||||
case Relationship.Ally:
|
||||
alternateViewColor = this.theme.allyColor();
|
||||
break;
|
||||
case Relationship.Enemy:
|
||||
alternateViewColor = this.theme.enemyColor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const sprite = getColoredSprite(
|
||||
unit,
|
||||
this.theme,
|
||||
alternateViewColor ?? customTerritoryColor,
|
||||
alternateViewColor,
|
||||
);
|
||||
|
||||
this.context.drawImage(
|
||||
sprite,
|
||||
|
||||
@@ -160,30 +160,7 @@ export class WinModal extends LitElement implements Layer {
|
||||
|
||||
innerHtml() {
|
||||
return html`
|
||||
<div style="text-align: center; margin: 15px 0; line-height: 1.5;">
|
||||
<p>
|
||||
<span style="color: red;">Time's running out!</span> <br />
|
||||
I need your support to continue working on OpenFront full-time. Please
|
||||
donate now to keep the updates, new features, and improvements coming.
|
||||
</p>
|
||||
<a
|
||||
href="https://patreon.com/OpenFront"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style="
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background-color: #FF424D;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s;
|
||||
"
|
||||
>
|
||||
Support on Patreon
|
||||
</a>
|
||||
</div>
|
||||
<div style="text-align: center; margin: 15px 0; line-height: 1.5;"></div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -48,8 +48,10 @@
|
||||
.left-gutter-ad {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 200px; /* Changed from top: 50% */
|
||||
transform: none; /* Removed translateY(-50%) since we don't need to center anymore */
|
||||
top: 200px;
|
||||
/* Changed from top: 50% */
|
||||
transform: none;
|
||||
/* Removed translateY(-50%) since we don't need to center anymore */
|
||||
z-index: 40;
|
||||
width: 300px;
|
||||
height: 600px;
|
||||
@@ -68,8 +70,10 @@
|
||||
.right-gutter-ad {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 200px; /* Changed from top: 50% */
|
||||
transform: none; /* Removed translateY(-50%) since we don't need to center anymore */
|
||||
top: 200px;
|
||||
/* Changed from top: 50% */
|
||||
transform: none;
|
||||
/* Removed translateY(-50%) since we don't need to center anymore */
|
||||
z-index: 40;
|
||||
width: 300px;
|
||||
height: 600px;
|
||||
@@ -137,6 +141,7 @@
|
||||
gtag("config", "G-WQGQQ8RDN4");
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body
|
||||
class="h-full select-none font-sans min-h-screen bg-opacity-0 bg-cover bg-center bg-fixed transition-opacity duration-300 ease-in-out flex flex-col"
|
||||
>
|
||||
@@ -353,6 +358,7 @@
|
||||
<help-modal></help-modal>
|
||||
<dark-mode-button></dark-mode-button>
|
||||
<user-setting></user-setting>
|
||||
<multi-tab-modal></multi-tab-modal>
|
||||
<div
|
||||
id="language-modal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex justify-center items-center"
|
||||
|
||||
@@ -5,6 +5,7 @@ import betweenTwoSeas from "../../../resources/maps/BetweenTwoSeasThumb.webp";
|
||||
import blackSea from "../../../resources/maps/BlackSeaThumb.webp";
|
||||
import britannia from "../../../resources/maps/BritanniaThumb.webp";
|
||||
import europe from "../../../resources/maps/EuropeThumb.webp";
|
||||
import faroeislands from "../../../resources/maps/FaroeIslandsThumb.webp";
|
||||
import gatewayToTheAtlantic from "../../../resources/maps/GatewayToTheAtlanticThumb.webp";
|
||||
import iceland from "../../../resources/maps/IcelandThumb.webp";
|
||||
import japan from "../../../resources/maps/JapanThumb.webp";
|
||||
@@ -57,6 +58,8 @@ export function getMapsImage(map: GameMapType): string {
|
||||
return betweenTwoSeas;
|
||||
case GameMapType.KnownWorld:
|
||||
return knownworld;
|
||||
case GameMapType.FaroeIslands:
|
||||
return faroeislands;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface NukeMagnitude {
|
||||
|
||||
export interface Config {
|
||||
samHittingChance(): number;
|
||||
samWarheadHittingChance(): number;
|
||||
spawnImmunityDuration(): Tick;
|
||||
serverConfig(): ServerConfig;
|
||||
gameConfig(): GameConfig;
|
||||
@@ -118,6 +119,7 @@ export interface Config {
|
||||
difficultyModifier(difficulty: Difficulty): number;
|
||||
// 0-1
|
||||
traitorDefenseDebuff(): number;
|
||||
traitorDuration(): number;
|
||||
nukeMagnitudes(unitType: UnitType): NukeMagnitude;
|
||||
defaultNukeSpeed(): number;
|
||||
nukeDeathFactor(humans: number, tilesOwned: number): number;
|
||||
|
||||
@@ -34,7 +34,7 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
||||
return process.env.GIT_COMMIT;
|
||||
}
|
||||
r2Endpoint(): string {
|
||||
return process.env.R2_ENDPOINT;
|
||||
return `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
||||
}
|
||||
r2AccessKey(): string {
|
||||
return process.env.R2_ACCESS_KEY;
|
||||
@@ -89,6 +89,7 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
||||
GameMapType.Mars,
|
||||
GameMapType.Oceania,
|
||||
GameMapType.Japan, // Japan at this level because its 2/3 water
|
||||
GameMapType.FaroeIslands,
|
||||
].includes(map)
|
||||
) {
|
||||
return Math.random() < 0.2 ? 70 : 40;
|
||||
@@ -135,8 +136,15 @@ export class DefaultConfig implements Config {
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
samWarheadHittingChance(): number {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
traitorDefenseDebuff(): number {
|
||||
return 0.8;
|
||||
return 0.5;
|
||||
}
|
||||
traitorDuration(): number {
|
||||
return 30 * 10; // 30 seconds
|
||||
}
|
||||
spawnImmunityDuration(): Tick {
|
||||
return 5 * 10;
|
||||
@@ -325,7 +333,7 @@ export class DefaultConfig implements Config {
|
||||
p.type() == PlayerType.Human && this.infiniteGold()
|
||||
? 0
|
||||
: Math.min(
|
||||
1_500_000 * 3,
|
||||
3_000_000,
|
||||
(p.unitsIncludingConstruction(UnitType.SAMLauncher).length +
|
||||
1) *
|
||||
1_500_000,
|
||||
|
||||
@@ -24,6 +24,14 @@ export class DevServerConfig extends DefaultServerConfig {
|
||||
return Math.random() < 0.5 ? 2 : 3;
|
||||
}
|
||||
|
||||
samWarheadHittingChance(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
samHittingChance(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
discordRedirectURI(): string {
|
||||
return "http://localhost:3000/auth/callback";
|
||||
}
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import {
|
||||
Execution,
|
||||
Game,
|
||||
Player,
|
||||
PlayerType,
|
||||
Relation,
|
||||
TerraNullius,
|
||||
Tick,
|
||||
} from "../game/Game";
|
||||
import { Execution, Game, Player } from "../game/Game";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { simpleHash } from "../Util";
|
||||
import { AttackExecution } from "./AttackExecution";
|
||||
import { BotBehavior } from "./utils/BotBehavior";
|
||||
|
||||
export class BotExecution implements Execution {
|
||||
private active = true;
|
||||
@@ -17,8 +9,7 @@ export class BotExecution implements Execution {
|
||||
private mg: Game;
|
||||
private neighborsTerraNullius = true;
|
||||
|
||||
private enemy: Player | null = null;
|
||||
private lastEnemyUpdateTick: Tick;
|
||||
private behavior: BotBehavior | null = null;
|
||||
private attackRate: number;
|
||||
private attackTick: number;
|
||||
private triggerRatio: number;
|
||||
@@ -36,7 +27,7 @@ export class BotExecution implements Execution {
|
||||
return false;
|
||||
}
|
||||
|
||||
init(mg: Game, ticks: number) {
|
||||
init(mg: Game) {
|
||||
this.mg = mg;
|
||||
this.bot.setTargetTroopRatio(0.7);
|
||||
}
|
||||
@@ -49,118 +40,47 @@ export class BotExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleAllianceRequests();
|
||||
if (this.behavior === null) {
|
||||
this.behavior = new BotBehavior(
|
||||
this.random,
|
||||
this.mg,
|
||||
this.bot,
|
||||
this.triggerRatio,
|
||||
this.reserveRatio,
|
||||
);
|
||||
}
|
||||
|
||||
this.behavior.handleAllianceRequests();
|
||||
this.maybeAttack();
|
||||
}
|
||||
|
||||
private handleAllianceRequests() {
|
||||
this.bot.incomingAllianceRequests().forEach((ar) => {
|
||||
const requestorIsMuchLarger =
|
||||
ar.requestor().numTilesOwned() > this.bot.numTilesOwned() * 3;
|
||||
if (
|
||||
ar.requestor().isTraitor() ||
|
||||
this.bot.relation(ar.requestor()) < Relation.Neutral ||
|
||||
(!requestorIsMuchLarger && ar.requestor().alliances().length >= 3)
|
||||
) {
|
||||
ar.reject();
|
||||
} else {
|
||||
ar.accept();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private maybeAttack() {
|
||||
const traitors = this.bot
|
||||
.neighbors()
|
||||
.filter((n) => n.isPlayer() && n.isTraitor()) as Player[];
|
||||
if (traitors.length > 0) {
|
||||
const toAttack = this.random.randElement(traitors);
|
||||
const odds = this.bot.isFriendly(toAttack) ? 6 : 3;
|
||||
if (this.random.chance(odds)) {
|
||||
this.behavior.sendAttack(toAttack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.neighborsTerraNullius) {
|
||||
for (const b of this.bot.borderTiles()) {
|
||||
for (const n of this.mg.neighbors(b)) {
|
||||
if (!this.mg.hasOwner(n) && this.mg.isLand(n)) {
|
||||
this.sendAttack(this.mg.terraNullius());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.bot.sharesBorderWith(this.mg.terraNullius())) {
|
||||
this.behavior.sendAttack(this.mg.terraNullius());
|
||||
return;
|
||||
}
|
||||
this.neighborsTerraNullius = false;
|
||||
}
|
||||
|
||||
if (this.mg.ticks() - this.lastEnemyUpdateTick > 100) {
|
||||
this.enemy = null;
|
||||
}
|
||||
|
||||
// Switch enemies if we're under attack
|
||||
const incomingAttacks = this.bot.incomingAttacks();
|
||||
if (incomingAttacks.length > 0) {
|
||||
this.enemy = incomingAttacks
|
||||
.sort((a, b) => b.troops() - a.troops())[0]
|
||||
.attacker();
|
||||
this.lastEnemyUpdateTick = this.mg.ticks();
|
||||
}
|
||||
|
||||
if (this.enemy === null) {
|
||||
// Save up troops until we reach the trigger ratio
|
||||
const maxPop = this.mg.config().maxPopulation(this.bot);
|
||||
const ratio = this.bot.population() / maxPop;
|
||||
if (ratio < this.triggerRatio) return;
|
||||
|
||||
// Choose a new enemy randomly
|
||||
const border = Array.from(this.bot.borderTiles())
|
||||
.flatMap((t) => this.mg.neighbors(t))
|
||||
.filter(
|
||||
(t) => this.mg.isLand(t) && this.mg.ownerID(t) != this.bot.smallID(),
|
||||
);
|
||||
if (border.length > 0) {
|
||||
const toAttack = this.random.randElement(border);
|
||||
const owner = this.mg.owner(toAttack);
|
||||
if (!owner.isPlayer()) {
|
||||
this.sendAttack(this.mg.terraNullius());
|
||||
this.neighborsTerraNullius = true;
|
||||
return;
|
||||
}
|
||||
this.enemy = owner;
|
||||
this.lastEnemyUpdateTick = this.mg.ticks();
|
||||
}
|
||||
|
||||
// Select a traitor as an enemy
|
||||
const traitors = this.bot
|
||||
.neighbors()
|
||||
.filter((n) => n.isPlayer() && n.isTraitor()) as Player[];
|
||||
if (traitors.length > 0) {
|
||||
const toAttack = this.random.randElement(traitors);
|
||||
const odds = this.bot.isFriendly(toAttack) ? 6 : 3;
|
||||
if (this.random.chance(odds)) {
|
||||
this.enemy = toAttack;
|
||||
this.lastEnemyUpdateTick = this.mg.ticks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.enemy) {
|
||||
if (this.bot.isFriendly(this.enemy) && this.random.chance(50)) {
|
||||
this.enemy = null;
|
||||
return;
|
||||
}
|
||||
if (this.enemy.type() == PlayerType.FakeHuman) {
|
||||
if (!this.random.chance(2)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.sendAttack(this.enemy);
|
||||
}
|
||||
}
|
||||
|
||||
private sendAttack(toAttack: Player | TerraNullius) {
|
||||
if (toAttack.isPlayer() && this.bot.isOnSameTeam(toAttack)) return;
|
||||
const maxPop = this.mg.config().maxPopulation(this.bot);
|
||||
const max = maxPop * this.bot.targetTroopRatio();
|
||||
const target = max * this.reserveRatio;
|
||||
const troops = this.bot.troops() - target;
|
||||
if (troops < 1) return;
|
||||
this.mg.addExecution(
|
||||
new AttackExecution(
|
||||
troops,
|
||||
this.bot.id(),
|
||||
toAttack.isPlayer() ? toAttack.id() : null,
|
||||
),
|
||||
);
|
||||
this.behavior.forgetOldEnemies();
|
||||
this.behavior.checkIncomingAttacks();
|
||||
const enemy = this.behavior.selectRandomEnemy();
|
||||
if (!enemy) return;
|
||||
if (!this.bot.sharesBorderWith(enemy)) return;
|
||||
this.behavior.sendAttack(enemy);
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { consolex } from "../Consolex";
|
||||
import {
|
||||
AllianceRequest,
|
||||
Cell,
|
||||
Difficulty,
|
||||
Execution,
|
||||
@@ -11,7 +10,6 @@ import {
|
||||
PlayerType,
|
||||
Relation,
|
||||
TerrainType,
|
||||
TerraNullius,
|
||||
Tick,
|
||||
Unit,
|
||||
UnitType,
|
||||
@@ -20,30 +18,28 @@ import { euclDistFN, manhattanDistFN, TileRef } from "../game/GameMap";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { GameID } from "../Schemas";
|
||||
import { calculateBoundingBox, simpleHash } from "../Util";
|
||||
import { AllianceRequestReplyExecution } from "./alliance/AllianceRequestReplyExecution";
|
||||
import { AttackExecution } from "./AttackExecution";
|
||||
import { ConstructionExecution } from "./ConstructionExecution";
|
||||
import { EmojiExecution } from "./EmojiExecution";
|
||||
import { NukeExecution } from "./NukeExecution";
|
||||
import { SpawnExecution } from "./SpawnExecution";
|
||||
import { TransportShipExecution } from "./TransportShipExecution";
|
||||
import { closestTwoTiles } from "./Util";
|
||||
import { BotBehavior } from "./utils/BotBehavior";
|
||||
|
||||
export class FakeHumanExecution implements Execution {
|
||||
private firstMove = true;
|
||||
|
||||
private active = true;
|
||||
private random: PseudoRandom;
|
||||
private behavior: BotBehavior | null = null;
|
||||
private mg: Game;
|
||||
private player: Player = null;
|
||||
|
||||
private enemy: Player | null = null;
|
||||
private attackRate: number;
|
||||
private attackTick: number;
|
||||
private triggerRatio: number;
|
||||
private reserveRatio: number;
|
||||
|
||||
private lastEnemyUpdateTick: number = 0;
|
||||
private lastEmojiSent = new Map<Player, Tick>();
|
||||
private lastNukeSent: [Tick, TileRef][] = [];
|
||||
private embargoMalusApplied = new Set<PlayerID>();
|
||||
@@ -61,7 +57,7 @@ export class FakeHumanExecution implements Execution {
|
||||
this.reserveRatio = this.random.nextInt(30, 60) / 100;
|
||||
}
|
||||
|
||||
init(mg: Game, ticks: number) {
|
||||
init(mg: Game) {
|
||||
this.mg = mg;
|
||||
if (this.random.chance(10)) {
|
||||
// this.isTraitor = true
|
||||
@@ -127,16 +123,29 @@ export class FakeHumanExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.firstMove) {
|
||||
this.firstMove = false;
|
||||
this.sendAttack(this.mg.terraNullius());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.player.isAlive()) {
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.behavior === null) {
|
||||
// Player is unavailable during init()
|
||||
this.behavior = new BotBehavior(
|
||||
this.random,
|
||||
this.mg,
|
||||
this.player,
|
||||
this.triggerRatio,
|
||||
this.reserveRatio,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.firstMove) {
|
||||
this.firstMove = false;
|
||||
this.behavior.sendAttack(this.mg.terraNullius());
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this.player.troops() > 100_000 &&
|
||||
this.player.targetTroopRatio() > 0.7
|
||||
@@ -145,7 +154,7 @@ export class FakeHumanExecution implements Execution {
|
||||
}
|
||||
|
||||
this.updateRelationsFromEmbargos();
|
||||
this.handleAllianceRequests();
|
||||
this.behavior.handleAllianceRequests();
|
||||
this.handleEnemies();
|
||||
this.handleUnits();
|
||||
this.handleEmbargoesToHostileNations();
|
||||
@@ -159,14 +168,6 @@ export class FakeHumanExecution implements Execution {
|
||||
(t) => this.mg.isLand(t) && this.mg.ownerID(t) != this.player.smallID(),
|
||||
);
|
||||
|
||||
const enemiesWithTN = enemyborder.map((t) =>
|
||||
this.mg.playerBySmallID(this.mg.ownerID(t)),
|
||||
);
|
||||
if (enemiesWithTN.filter((o) => !o.isPlayer()).length > 0) {
|
||||
this.sendAttack(this.mg.terraNullius());
|
||||
return;
|
||||
}
|
||||
|
||||
if (enemyborder.length == 0) {
|
||||
if (this.random.chance(10)) {
|
||||
this.sendBoatRandomly();
|
||||
@@ -178,17 +179,24 @@ export class FakeHumanExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
|
||||
const enemiesWithTN = enemyborder.map((t) =>
|
||||
this.mg.playerBySmallID(this.mg.ownerID(t)),
|
||||
);
|
||||
if (enemiesWithTN.filter((o) => !o.isPlayer()).length > 0) {
|
||||
this.behavior.sendAttack(this.mg.terraNullius());
|
||||
return;
|
||||
}
|
||||
|
||||
const enemies = enemiesWithTN
|
||||
.filter((o) => o.isPlayer())
|
||||
.sort((a, b) => a.troops() - b.troops());
|
||||
|
||||
// 5% chance to send a random alliance request
|
||||
if (this.random.chance(20)) {
|
||||
for (const toAlly of enemies) {
|
||||
if (this.player.canSendAllianceRequest(toAlly)) {
|
||||
this.player.createAllianceRequest(toAlly);
|
||||
return;
|
||||
}
|
||||
const toAlly = this.random.randElement(enemies);
|
||||
if (this.player.canSendAllianceRequest(toAlly)) {
|
||||
this.player.createAllianceRequest(toAlly);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +205,7 @@ export class FakeHumanExecution implements Execution {
|
||||
? enemies[0]
|
||||
: this.random.randElement(enemies);
|
||||
if (this.shouldAttack(toAttack)) {
|
||||
this.sendAttack(toAttack);
|
||||
this.behavior.sendAttack(toAttack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,77 +242,29 @@ export class FakeHumanExecution implements Execution {
|
||||
}
|
||||
|
||||
handleEnemies() {
|
||||
if (this.mg.ticks() - this.lastEnemyUpdateTick > 100) {
|
||||
this.enemy = null;
|
||||
}
|
||||
|
||||
// Switch enemies if we're under attack
|
||||
const incomingAttacks = this.player.incomingAttacks();
|
||||
if (incomingAttacks.length > 0) {
|
||||
this.enemy = incomingAttacks
|
||||
.sort((a, b) => b.troops() - a.troops())[0]
|
||||
.attacker();
|
||||
this.lastEnemyUpdateTick = this.mg.ticks();
|
||||
}
|
||||
|
||||
// Attack allies' targets
|
||||
outer: for (const ally of this.player.allies()) {
|
||||
if (this.player.relation(ally) < Relation.Friendly) continue;
|
||||
for (const target of ally.targets()) {
|
||||
if (target === this.player) continue;
|
||||
if (this.player.isAlliedWith(target)) continue;
|
||||
|
||||
this.player.updateRelation(ally, -20);
|
||||
this.enemy = target;
|
||||
this.lastEnemyUpdateTick = this.mg.ticks();
|
||||
if (ally.type() == PlayerType.Human) {
|
||||
this.mg.addExecution(
|
||||
new EmojiExecution(this.player.id(), ally.id(), "👍"),
|
||||
);
|
||||
}
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.enemy === null) {
|
||||
// Save up troops until we reach the trigger ratio
|
||||
const maxPop = this.mg.config().maxPopulation(this.player);
|
||||
const ratio = this.player.population() / maxPop;
|
||||
if (ratio < this.triggerRatio) return;
|
||||
|
||||
// Choose a new enemy
|
||||
const mostHated = this.player.allRelationsSorted()[0] ?? null;
|
||||
if (mostHated != null && mostHated.relation == Relation.Hostile) {
|
||||
this.enemy = mostHated.player;
|
||||
this.lastEnemyUpdateTick = this.mg.ticks();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.enemy) {
|
||||
if (this.player.isFriendly(this.enemy)) {
|
||||
this.enemy = null;
|
||||
return;
|
||||
}
|
||||
this.maybeSendEmoji();
|
||||
this.maybeSendNuke(this.enemy);
|
||||
if (this.player.sharesBorderWith(this.enemy)) {
|
||||
this.sendAttack(this.enemy);
|
||||
} else {
|
||||
this.maybeSendBoatAttack(this.enemy);
|
||||
}
|
||||
return;
|
||||
this.behavior.forgetOldEnemies();
|
||||
this.behavior.checkIncomingAttacks();
|
||||
this.behavior.assistAllies();
|
||||
const enemy = this.behavior.selectEnemy();
|
||||
if (!enemy) return;
|
||||
this.maybeSendEmoji(enemy);
|
||||
this.maybeSendNuke(enemy);
|
||||
if (this.player.sharesBorderWith(enemy)) {
|
||||
this.behavior.sendAttack(enemy);
|
||||
} else {
|
||||
this.maybeSendBoatAttack(enemy);
|
||||
}
|
||||
}
|
||||
|
||||
private maybeSendEmoji() {
|
||||
if (this.enemy.type() != PlayerType.Human) return;
|
||||
const lastSent = this.lastEmojiSent.get(this.enemy) ?? -300;
|
||||
private maybeSendEmoji(enemy: Player) {
|
||||
if (enemy.type() != PlayerType.Human) return;
|
||||
const lastSent = this.lastEmojiSent.get(enemy) ?? -300;
|
||||
if (this.mg.ticks() - lastSent <= 300) return;
|
||||
this.lastEmojiSent.set(this.enemy, this.mg.ticks());
|
||||
this.lastEmojiSent.set(enemy, this.mg.ticks());
|
||||
this.mg.addExecution(
|
||||
new EmojiExecution(
|
||||
this.player.id(),
|
||||
this.enemy.id(),
|
||||
enemy.id(),
|
||||
this.random.randElement(["🤡", "😡"]),
|
||||
),
|
||||
);
|
||||
@@ -315,6 +275,7 @@ export class FakeHumanExecution implements Execution {
|
||||
if (
|
||||
silos.length == 0 ||
|
||||
this.player.gold() < this.cost(UnitType.AtomBomb) ||
|
||||
other.type() == PlayerType.Bot ||
|
||||
this.player.isOnSameTeam(other)
|
||||
) {
|
||||
return;
|
||||
@@ -402,7 +363,8 @@ export class FakeHumanExecution implements Execution {
|
||||
// Prefer tiles that are closer to a silo
|
||||
const siloTiles = silos.map((u) => u.tile());
|
||||
const { x: closestSilo } = closestTwoTiles(this.mg, siloTiles, [tile]);
|
||||
const distanceToClosestSilo = this.mg.euclideanDist(tile, closestSilo);
|
||||
const distanceSquared = this.mg.euclideanDistSquared(tile, closestSilo);
|
||||
const distanceToClosestSilo = Math.sqrt(distanceSquared);
|
||||
tileValue -= distanceToClosestSilo * 30;
|
||||
|
||||
// Don't target near recent targets
|
||||
@@ -558,36 +520,6 @@ export class FakeHumanExecution implements Execution {
|
||||
return this.mg.unitInfo(type).cost(this.player);
|
||||
}
|
||||
|
||||
private handleAllianceRequests() {
|
||||
for (const req of this.player.incomingAllianceRequests()) {
|
||||
if (req.requestor().isTraitor()) {
|
||||
this.replyToAllianceRequest(req, false);
|
||||
continue;
|
||||
}
|
||||
if (this.player.relation(req.requestor()) < Relation.Neutral) {
|
||||
this.replyToAllianceRequest(req, false);
|
||||
continue;
|
||||
}
|
||||
const requestorIsMuchLarger =
|
||||
req.requestor().numTilesOwned() > this.player.numTilesOwned() * 3;
|
||||
if (!requestorIsMuchLarger && req.requestor().alliances().length >= 3) {
|
||||
this.replyToAllianceRequest(req, false);
|
||||
continue;
|
||||
}
|
||||
this.replyToAllianceRequest(req, true);
|
||||
}
|
||||
}
|
||||
|
||||
private replyToAllianceRequest(req: AllianceRequest, accept: boolean): void {
|
||||
this.mg.addExecution(
|
||||
new AllianceRequestReplyExecution(
|
||||
req.requestor().id(),
|
||||
this.player.id(),
|
||||
accept,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
sendBoatRandomly() {
|
||||
const oceanShore = Array.from(this.player.borderTiles()).filter((t) =>
|
||||
this.mg.isOceanShore(t),
|
||||
@@ -639,23 +571,6 @@ export class FakeHumanExecution implements Execution {
|
||||
return null;
|
||||
}
|
||||
|
||||
sendAttack(toAttack: Player | TerraNullius) {
|
||||
if (toAttack.isPlayer() && this.player.isOnSameTeam(toAttack)) return;
|
||||
const max =
|
||||
this.mg.config().maxPopulation(this.player) *
|
||||
this.player.targetTroopRatio();
|
||||
const target = max * this.reserveRatio;
|
||||
const troops = this.player.troops() - target;
|
||||
if (troops < 1) return;
|
||||
this.mg.addExecution(
|
||||
new AttackExecution(
|
||||
troops,
|
||||
this.player.id(),
|
||||
toAttack.isPlayer() ? toAttack.id() : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private randOceanShoreTile(tile: TileRef, dist: number): TileRef | null {
|
||||
const x = this.mg.x(tile);
|
||||
const y = this.mg.y(tile);
|
||||
|
||||
@@ -170,7 +170,6 @@ export class MirvExecution implements Execution {
|
||||
if (!this.mg.isValidCoord(x, y)) {
|
||||
continue;
|
||||
}
|
||||
console.log(`got coord ${x}, ${y}`);
|
||||
const tile = this.mg.ref(x, y);
|
||||
if (!this.mg.isLand(tile)) {
|
||||
continue;
|
||||
|
||||
@@ -19,8 +19,13 @@ export class SAMLauncherExecution implements Execution {
|
||||
private active: boolean = true;
|
||||
|
||||
private target: Unit = null;
|
||||
private warheadTargets: Unit[] = [];
|
||||
|
||||
private searchRangeRadius = 75;
|
||||
private searchRangeRadius = 80;
|
||||
// As MIRV go very fast we have to detect them very early but we only
|
||||
// shoot the one targeting very close (MIRVWarheadProtectionRadius)
|
||||
private MIRVWarheadSearchRadius = 400;
|
||||
private MIRVWarheadProtectionRadius = 50;
|
||||
|
||||
private pseudoRandom: PseudoRandom;
|
||||
|
||||
@@ -39,6 +44,52 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.player = mg.player(this.ownerId);
|
||||
}
|
||||
|
||||
private getSingleTarget(): Unit | null {
|
||||
const nukes = this.mg
|
||||
.nearbyUnits(this.sam.tile(), this.searchRangeRadius, [
|
||||
UnitType.AtomBomb,
|
||||
UnitType.HydrogenBomb,
|
||||
])
|
||||
.filter(
|
||||
({ unit }) =>
|
||||
unit.owner() !== this.player && !this.player.isFriendly(unit.owner()),
|
||||
);
|
||||
|
||||
return (
|
||||
nukes.sort((a, b) => {
|
||||
const { unit: unitA, distSquared: distA } = a;
|
||||
const { unit: unitB, distSquared: distB } = b;
|
||||
|
||||
// Prioritize Hydrogen Bombs
|
||||
if (
|
||||
unitA.type() === UnitType.HydrogenBomb &&
|
||||
unitB.type() !== UnitType.HydrogenBomb
|
||||
)
|
||||
return -1;
|
||||
if (
|
||||
unitA.type() !== UnitType.HydrogenBomb &&
|
||||
unitB.type() === UnitType.HydrogenBomb
|
||||
)
|
||||
return 1;
|
||||
|
||||
// If both are the same type, sort by distance (lower `distSquared` means closer)
|
||||
return distA - distB;
|
||||
})[0]?.unit ?? null
|
||||
);
|
||||
}
|
||||
|
||||
private isHit(type: UnitType, random: number): boolean {
|
||||
if (type == UnitType.AtomBomb) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == UnitType.MIRVWarhead) {
|
||||
return random < this.mg.config().samWarheadHittingChance();
|
||||
}
|
||||
|
||||
return random < this.mg.config().samHittingChance();
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.sam == null) {
|
||||
const spawnTile = this.player.canBuild(UnitType.SAMLauncher, this.tile);
|
||||
@@ -64,36 +115,26 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.pseudoRandom = new PseudoRandom(this.sam.id());
|
||||
}
|
||||
|
||||
const nukes = this.mg
|
||||
.nearbyUnits(this.sam.tile(), this.searchRangeRadius, [
|
||||
UnitType.AtomBomb,
|
||||
UnitType.HydrogenBomb,
|
||||
])
|
||||
this.warheadTargets = this.mg
|
||||
.nearbyUnits(
|
||||
this.sam.tile(),
|
||||
this.MIRVWarheadSearchRadius,
|
||||
UnitType.MIRVWarhead,
|
||||
)
|
||||
.map(({ unit }) => unit)
|
||||
.filter(
|
||||
({ unit }) =>
|
||||
(unit) =>
|
||||
unit.owner() !== this.player && !this.player.isFriendly(unit.owner()),
|
||||
)
|
||||
.filter(
|
||||
(unit) =>
|
||||
this.mg.manhattanDist(unit.detonationDst(), this.sam.tile()) <
|
||||
this.MIRVWarheadProtectionRadius,
|
||||
);
|
||||
|
||||
this.target =
|
||||
nukes.sort((a, b) => {
|
||||
const { unit: unitA, distSquared: distA } = a;
|
||||
const { unit: unitB, distSquared: distB } = b;
|
||||
|
||||
// Prioritize Hydrogen Bombs
|
||||
if (
|
||||
unitA.type() === UnitType.HydrogenBomb &&
|
||||
unitB.type() !== UnitType.HydrogenBomb
|
||||
)
|
||||
return -1;
|
||||
if (
|
||||
unitA.type() !== UnitType.HydrogenBomb &&
|
||||
unitB.type() === UnitType.HydrogenBomb
|
||||
)
|
||||
return 1;
|
||||
|
||||
// If both are the same type, sort by distance (lower `distSquared` means closer)
|
||||
return distA - distB;
|
||||
})[0]?.unit ?? null;
|
||||
if (this.warheadTargets.length == 0) {
|
||||
this.target = this.getSingleTarget();
|
||||
}
|
||||
|
||||
if (
|
||||
this.sam.isCooldown() &&
|
||||
@@ -102,29 +143,46 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.sam.setCooldown(false);
|
||||
}
|
||||
|
||||
if (this.target && !this.sam.isCooldown() && !this.target.targetedBySAM()) {
|
||||
const isSingleTarget = this.target && !this.target.targetedBySAM();
|
||||
if (
|
||||
(isSingleTarget || this.warheadTargets.length > 0) &&
|
||||
!this.sam.isCooldown()
|
||||
) {
|
||||
this.sam.setCooldown(true);
|
||||
const type =
|
||||
this.warheadTargets.length > 0
|
||||
? UnitType.MIRVWarhead
|
||||
: this.target.type();
|
||||
const random = this.pseudoRandom.next();
|
||||
let hit = true;
|
||||
if (this.target.type() != UnitType.AtomBomb) {
|
||||
hit = random < this.mg.config().samHittingChance();
|
||||
}
|
||||
const hit = this.isHit(type, random);
|
||||
if (!hit) {
|
||||
this.mg.displayMessage(
|
||||
`Missile failed to intercept ${this.target.type()}`,
|
||||
`Missile failed to intercept ${type}`,
|
||||
MessageType.ERROR,
|
||||
this.sam.owner().id(),
|
||||
);
|
||||
} else {
|
||||
this.target.setTargetedBySAM(true);
|
||||
this.mg.addExecution(
|
||||
new SAMMissileExecution(
|
||||
this.sam.tile(),
|
||||
this.sam.owner(),
|
||||
this.sam,
|
||||
this.target,
|
||||
),
|
||||
);
|
||||
if (this.warheadTargets.length > 0) {
|
||||
// Message
|
||||
this.mg.displayMessage(
|
||||
`${this.warheadTargets.length} MIRV warheads intercepted`,
|
||||
MessageType.SUCCESS,
|
||||
this.sam.owner().id(),
|
||||
);
|
||||
// Delete warheads
|
||||
this.warheadTargets.forEach((u) => u.delete());
|
||||
} else {
|
||||
this.target.setTargetedBySAM(true);
|
||||
this.mg.addExecution(
|
||||
new SAMMissileExecution(
|
||||
this.sam.tile(),
|
||||
this.sam.owner(),
|
||||
this.sam,
|
||||
this.target,
|
||||
),
|
||||
);
|
||||
this.warheadTargets = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
import {
|
||||
AllianceRequest,
|
||||
Game,
|
||||
Player,
|
||||
PlayerType,
|
||||
Relation,
|
||||
TerraNullius,
|
||||
Tick,
|
||||
} from "../../game/Game";
|
||||
import { PseudoRandom } from "../../PseudoRandom";
|
||||
import { AttackExecution } from "../AttackExecution";
|
||||
import { EmojiExecution } from "../EmojiExecution";
|
||||
|
||||
export class BotBehavior {
|
||||
private enemy: Player | null = null;
|
||||
private enemyUpdated: Tick;
|
||||
|
||||
constructor(
|
||||
private random: PseudoRandom,
|
||||
private game: Game,
|
||||
private player: Player,
|
||||
private triggerRatio: number,
|
||||
private reserveRatio: number,
|
||||
) {}
|
||||
|
||||
handleAllianceRequests() {
|
||||
for (const req of this.player.incomingAllianceRequests()) {
|
||||
if (shouldAcceptAllianceRequest(this.player, req)) {
|
||||
req.accept();
|
||||
} else {
|
||||
req.reject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private emoji(player: Player, emoji: string) {
|
||||
if (player.type() !== PlayerType.Human) return;
|
||||
this.game.addExecution(
|
||||
new EmojiExecution(this.player.id(), player.id(), emoji),
|
||||
);
|
||||
}
|
||||
|
||||
forgetOldEnemies() {
|
||||
// Forget old enemies
|
||||
if (this.game.ticks() - this.enemyUpdated > 100) {
|
||||
this.enemy = null;
|
||||
}
|
||||
}
|
||||
|
||||
checkIncomingAttacks() {
|
||||
// Switch enemies if we're under attack
|
||||
const incomingAttacks = this.player.incomingAttacks();
|
||||
if (incomingAttacks.length > 0) {
|
||||
this.enemy = incomingAttacks
|
||||
.sort((a, b) => b.troops() - a.troops())[0]
|
||||
.attacker();
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
}
|
||||
}
|
||||
|
||||
assistAllies() {
|
||||
outer: for (const ally of this.player.allies()) {
|
||||
if (ally.targets().length === 0) continue;
|
||||
if (this.player.relation(ally) < Relation.Friendly) {
|
||||
// this.emoji(ally, "🤦");
|
||||
continue;
|
||||
}
|
||||
for (const target of ally.targets()) {
|
||||
if (target === this.player) {
|
||||
// this.emoji(ally, "💀");
|
||||
continue;
|
||||
}
|
||||
if (this.player.isAlliedWith(target)) {
|
||||
// this.emoji(ally, "👎");
|
||||
continue;
|
||||
}
|
||||
// All checks passed, assist them
|
||||
this.player.updateRelation(ally, -20);
|
||||
this.enemy = target;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
this.emoji(ally, "👍");
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectEnemy(): Player | null {
|
||||
if (this.enemy === null) {
|
||||
// Save up troops until we reach the trigger ratio
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const ratio = this.player.population() / maxPop;
|
||||
if (ratio < this.triggerRatio) return null;
|
||||
}
|
||||
|
||||
// Prefer neighboring bots
|
||||
if (this.enemy === null) {
|
||||
const bots = this.player
|
||||
.neighbors()
|
||||
.filter((n) => n.isPlayer() && n.type() === PlayerType.Bot) as Player[];
|
||||
if (bots.length > 0) {
|
||||
const density = (p: Player) => p.troops() / p.numTilesOwned();
|
||||
this.enemy = bots.sort((a, b) => density(a) - density(b))[0];
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
}
|
||||
}
|
||||
|
||||
// Select the most hated player
|
||||
if (this.enemy === null) {
|
||||
const mostHated = this.player.allRelationsSorted()[0] ?? null;
|
||||
if (mostHated != null && mostHated.relation === Relation.Hostile) {
|
||||
this.enemy = mostHated.player;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check, don't attack our allies or teammates
|
||||
if (this.enemy && this.player.isFriendly(this.enemy)) {
|
||||
this.enemy = null;
|
||||
}
|
||||
return this.enemy;
|
||||
}
|
||||
|
||||
selectRandomEnemy(): Player | TerraNullius | null {
|
||||
if (this.enemy === null) {
|
||||
// Save up troops until we reach the trigger ratio
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const ratio = this.player.population() / maxPop;
|
||||
if (ratio < this.triggerRatio) return null;
|
||||
|
||||
// Choose a new enemy randomly
|
||||
const neighbors = this.player.neighbors();
|
||||
for (const neighbor of this.random.shuffleArray(neighbors)) {
|
||||
if (neighbor.isPlayer()) {
|
||||
if (this.player.isFriendly(neighbor)) continue;
|
||||
if (neighbor.type() == PlayerType.FakeHuman) {
|
||||
if (this.random.chance(2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.enemy = neighbor;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
}
|
||||
|
||||
// Select a traitor as an enemy
|
||||
const traitors = this.player
|
||||
.neighbors()
|
||||
.filter((n) => n.isPlayer() && n.isTraitor()) as Player[];
|
||||
if (traitors.length > 0) {
|
||||
const toAttack = this.random.randElement(traitors);
|
||||
const odds = this.player.isFriendly(toAttack) ? 6 : 3;
|
||||
if (this.random.chance(odds)) {
|
||||
this.enemy = toAttack;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check, don't attack our allies or teammates
|
||||
if (this.enemy && this.player.isFriendly(this.enemy)) {
|
||||
this.enemy = null;
|
||||
}
|
||||
return this.enemy;
|
||||
}
|
||||
|
||||
sendAttack(target: Player | TerraNullius) {
|
||||
if (target.isPlayer() && this.player.isOnSameTeam(target)) return;
|
||||
const maxPop = this.game.config().maxPopulation(this.player);
|
||||
const maxTroops = maxPop * this.player.targetTroopRatio();
|
||||
const targetTroops = maxTroops * this.reserveRatio;
|
||||
const troops = this.player.troops() - targetTroops;
|
||||
if (troops < 1) return;
|
||||
this.game.addExecution(
|
||||
new AttackExecution(
|
||||
troops,
|
||||
this.player.id(),
|
||||
target.isPlayer() ? target.id() : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function shouldAcceptAllianceRequest(player: Player, request: AllianceRequest) {
|
||||
const notTraitor = !request.requestor().isTraitor();
|
||||
const noMalice = player.relation(request.requestor()) >= Relation.Neutral;
|
||||
const requestorIsMuchLarger =
|
||||
request.requestor().numTilesOwned() > player.numTilesOwned() * 3;
|
||||
const notTooManyAlliances =
|
||||
requestorIsMuchLarger || request.requestor().alliances().length < 3;
|
||||
return notTraitor && noMalice && notTooManyAlliances;
|
||||
}
|
||||
@@ -67,6 +67,7 @@ export enum GameMapType {
|
||||
Japan = "Japan",
|
||||
BetweenTwoSeas = "Between Two Seas",
|
||||
KnownWorld = "Known World",
|
||||
FaroeIslands = "FaroeIslands",
|
||||
}
|
||||
|
||||
export enum GameType {
|
||||
@@ -314,6 +315,7 @@ export interface Player {
|
||||
// State & Properties
|
||||
isAlive(): boolean;
|
||||
isTraitor(): boolean;
|
||||
markTraitor(): void;
|
||||
largestClusterBoundingBox: { min: Cell; max: Cell } | null;
|
||||
lastTileChange(): Tick;
|
||||
|
||||
@@ -414,7 +416,7 @@ export interface Player {
|
||||
// Misc
|
||||
toUpdate(): PlayerUpdate;
|
||||
playerProfile(): PlayerProfile;
|
||||
canBoat(tile: TileRef): boolean;
|
||||
canBoat(tile: TileRef): TileRef | false;
|
||||
tradingPorts(port: Unit): Unit[];
|
||||
}
|
||||
|
||||
@@ -472,7 +474,7 @@ export interface Game extends GameMap {
|
||||
}
|
||||
|
||||
export interface PlayerActions {
|
||||
canBoat: boolean;
|
||||
canBoat: TileRef | false;
|
||||
canAttack: boolean;
|
||||
buildableUnits: BuildableUnit[];
|
||||
canSendEmojiAllPlayers: boolean;
|
||||
|
||||
@@ -518,7 +518,7 @@ export class GameImpl implements Game {
|
||||
);
|
||||
}
|
||||
if (!other.isTraitor()) {
|
||||
(breaker as PlayerImpl).isTraitor_ = true;
|
||||
breaker.markTraitor();
|
||||
}
|
||||
|
||||
const breakerSet = new Set(breaker.alliances());
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
Attack,
|
||||
Cell,
|
||||
EmojiMessage,
|
||||
GameMode,
|
||||
Gold,
|
||||
MessageType,
|
||||
MutableAlliance,
|
||||
@@ -67,7 +68,7 @@ export class PlayerImpl implements Player {
|
||||
// 0 to 100
|
||||
private _targetTroopRatio: bigint;
|
||||
|
||||
isTraitor_ = false;
|
||||
markedTraitorTick = -1;
|
||||
|
||||
private embargoes: Set<PlayerID> = new Set();
|
||||
|
||||
@@ -243,7 +244,7 @@ export class PlayerImpl implements Player {
|
||||
const ns: Set<Player | TerraNullius> = new Set();
|
||||
for (const border of this.borderTiles()) {
|
||||
for (const neighbor of this.mg.map().neighbors(border)) {
|
||||
if (this.mg.map().isLake(neighbor)) {
|
||||
if (this.mg.map().isLand(neighbor)) {
|
||||
const owner = this.mg.map().ownerID(neighbor);
|
||||
if (owner != this.smallID()) {
|
||||
ns.add(
|
||||
@@ -372,7 +373,14 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
isTraitor(): boolean {
|
||||
return this.isTraitor_;
|
||||
return (
|
||||
this.markedTraitorTick >= 0 &&
|
||||
this.mg.ticks() - this.markedTraitorTick <
|
||||
this.mg.config().traitorDuration()
|
||||
);
|
||||
}
|
||||
markTraitor(): void {
|
||||
this.markedTraitorTick = this.mg.ticks();
|
||||
}
|
||||
|
||||
createAllianceRequest(recipient: Player): AllianceRequest {
|
||||
@@ -516,6 +524,13 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
canDonate(recipient: Player): boolean {
|
||||
if (
|
||||
recipient.type() == PlayerType.Human &&
|
||||
this.mg.config().gameConfig().gameMode == GameMode.FFA
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.isFriendly(recipient)) {
|
||||
return false;
|
||||
}
|
||||
@@ -541,7 +556,7 @@ export class PlayerImpl implements Player {
|
||||
this.id(),
|
||||
);
|
||||
this.mg.displayMessage(
|
||||
`Recieved ${renderTroops(troops)} troops from ${this.name()}`,
|
||||
`Received ${renderTroops(troops)} troops from ${this.name()}`,
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
@@ -555,7 +570,7 @@ export class PlayerImpl implements Player {
|
||||
this.id(),
|
||||
);
|
||||
this.mg.displayMessage(
|
||||
`Recieved ${renderNumber(gold)} gold from ${this.name()}`,
|
||||
`Received ${renderNumber(gold)} gold from ${this.name()}`,
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
@@ -877,7 +892,7 @@ export class PlayerImpl implements Player {
|
||||
return rel;
|
||||
}
|
||||
|
||||
public canBoat(tile: TileRef): boolean {
|
||||
public canBoat(tile: TileRef): TileRef | false {
|
||||
if (
|
||||
this.units(UnitType.TransportShip).length >=
|
||||
this.mg.config().boatMaxNumber()
|
||||
@@ -920,7 +935,7 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
if (myPlayerBordersOcean && otherPlayerBordersOcean) {
|
||||
return this.canBuild(UnitType.TransportShip, dst) != false;
|
||||
return this.canBuild(UnitType.TransportShip, dst);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -943,7 +958,7 @@ export class PlayerImpl implements Player {
|
||||
|
||||
for (const t of sorted) {
|
||||
if (this.mg.owner(t) == this) {
|
||||
return this.canBuild(UnitType.TransportShip, dst) != false;
|
||||
return this.canBuild(UnitType.TransportShip, dst);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -41,6 +41,7 @@ const MAP_FILE_NAMES: Record<GameMapType, string> = {
|
||||
[GameMapType.Japan]: "Japan",
|
||||
[GameMapType.BetweenTwoSeas]: "BetweenTwoSeas",
|
||||
[GameMapType.KnownWorld]: "KnownWorld",
|
||||
[GameMapType.FaroeIslands]: "FaroeIslands",
|
||||
};
|
||||
|
||||
class GameMapLoader {
|
||||
|
||||
@@ -141,7 +141,7 @@ export class UnitImpl implements Unit {
|
||||
this._active = false;
|
||||
this.mg.addUpdate(this.toUpdate());
|
||||
this.mg.removeUnit(this);
|
||||
if (displayMessage) {
|
||||
if (displayMessage && this.type() != UnitType.MIRVWarhead) {
|
||||
this.mg.displayMessage(
|
||||
`Your ${this.type()} was destroyed`,
|
||||
MessageType.ERROR,
|
||||
|
||||
@@ -22,6 +22,7 @@ const maps = [
|
||||
"BetweenTwoSeas",
|
||||
"Japan",
|
||||
"KnownWorld",
|
||||
"FaroeIslands",
|
||||
];
|
||||
|
||||
const removeSmall = true;
|
||||
|
||||
@@ -86,8 +86,8 @@ export class MapPlaylist {
|
||||
Africa: 2,
|
||||
Britannia: 1,
|
||||
GatewayToTheAtlantic: 2,
|
||||
Australia: 1,
|
||||
Iceland: 1,
|
||||
Australia: 2,
|
||||
Iceland: 2,
|
||||
SouthAmerica: 3,
|
||||
KnownWorld: 2,
|
||||
};
|
||||
@@ -101,6 +101,7 @@ export class MapPlaylist {
|
||||
BetweenTwoSeas: 3,
|
||||
Japan: 3,
|
||||
BlackSea: 1,
|
||||
FaroeIslands: 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,13 @@ echo "Container name: ${CONTAINER_NAME}"
|
||||
echo "Docker image: ${FULL_IMAGE_NAME}"
|
||||
|
||||
# Load environment variables if .env exists
|
||||
if [ -f /root/.env ]; then
|
||||
if [ -f /home/openfront/.env ]; then
|
||||
echo "Loading environment variables from .env file..."
|
||||
export $(grep -v '^#' /root/.env | xargs)
|
||||
export $(grep -v '^#' /home/openfront/.env | xargs)
|
||||
fi
|
||||
|
||||
docker login -u $DOCKER_USERNAME -p $DOCKER_TOKEN
|
||||
|
||||
# Install Loki Docker plugin if not already installed
|
||||
if ! docker plugin ls | grep -q "loki"; then
|
||||
echo "Installing Loki Docker plugin..."
|
||||
@@ -99,7 +101,7 @@ docker run -d -p 80:80 -p 127.0.0.1:9090:9090 \
|
||||
--log-opt loki-external-labels="job=docker,environment=${ENV},host=${REGION},region=${REGION}" \
|
||||
--env GAME_ENV=${ENV} \
|
||||
--env REGION=${REGION} \
|
||||
--env-file /root/.env \
|
||||
--env-file /home/openfront/.env \
|
||||
--name ${CONTAINER_NAME} \
|
||||
$FULL_IMAGE_NAME
|
||||
|
||||
|
||||
Reference in New Issue
Block a user