A RuneTek3 client (377) that is deobfuscated, converted to Kotlin, and includes QoL improvements.
1stages:
2 - build
3 - publish
4
5variables:
6 GRADLE_OPTS: "-Dorg.gradle.daemon=false"
7 PACKAGE_NAME: "hla-client"
8 JAR_NAME: "377.jar"
9 # Server connection defaults (same across regions)
10 GAME_PORT: "43594"
11 ONDEMAND_PORT: "43594"
12 JAGGRAB_PORT: "43595"
13 HTTP_PORT: "8080"
14 RSA_PUB: "65537"
15
16# Per-region build. Each matrix row produces hla-<REGION>-client.zip with the
17# correct server host + RSA modulus baked into config/client-config.yaml.
18#
19# CI/CD variables required (set in Settings → CI/CD → Variables, regular type):
20# HLA_HOST_USE # e.g. use.objects.pw
21# HLA_HOST_USW # e.g. usw.objects.pw
22# HLA_RSA_MODULUS_USE # decimal modulus from rsa.yml on use.objects.pw
23# HLA_RSA_MODULUS_USW # decimal modulus from rsa.yml on usw.objects.pw
24#
25# To add a region: append a row to the matrix below and add HLA_HOST_<X> +
26# HLA_RSA_MODULUS_<X> variables. Modulus values are public (anyone connecting
27# sees them) so they don't need to be masked.
28build:
29 stage: build
30 image: eclipse-temurin:21-jdk-alpine
31 tags:
32 - docker
33 parallel:
34 matrix:
35 - REGION: "us-east"
36 - REGION: "us-west"
37 before_script:
38 - apk add --no-cache zip
39 - chmod +x gradlew
40 script:
41 # Resolve region-specific values from CI variables
42 - |
43 case "$REGION" in
44 us-east)
45 HOST="${HLA_HOST_USE:?HLA_HOST_USE not set in CI vars}"
46 MODULUS="${HLA_RSA_MODULUS_USE:?HLA_RSA_MODULUS_USE not set in CI vars}"
47 ;;
48 us-west)
49 HOST="${HLA_HOST_USW:?HLA_HOST_USW not set in CI vars}"
50 MODULUS="${HLA_RSA_MODULUS_USW:?HLA_RSA_MODULUS_USW not set in CI vars}"
51 ;;
52 *)
53 echo "Unknown region: $REGION" >&2
54 exit 1
55 ;;
56 esac
57 export HOST MODULUS
58
59 - export ZIP_NAME="hla-${REGION}-client.zip"
60 - echo "Building ${ZIP_NAME} for ${HOST} (modulus ${MODULUS:0:16}…)"
61
62 # Build the fat jar (same for all regions)
63 - ./gradlew clean jar
64
65 # Stage the dist directory
66 - mkdir -p dist/config
67 - cp build/libs/${JAR_NAME} dist/
68
69 # Generate region-specific client-config.yaml
70 - |
71 cat > dist/config/client-config.yaml <<EOF
72 # Generated by CI for region: ${REGION}
73 # Server: ${HOST}
74 # Build: ${CI_COMMIT_SHORT_SHA}
75 net:
76 address: ${HOST}
77 game_port: ${GAME_PORT}
78 ondemand_port: ${ONDEMAND_PORT}
79 jaggrab_port: ${JAGGRAB_PORT}
80 http_port: ${HTTP_PORT}
81 cache:
82 cacheDir: ./.377
83 jaggrabEnabled: true
84 rsa:
85 rsaEnabled: true
86 rsaPub: ${RSA_PUB}
87 rsaModulus: ${MODULUS}
88 login:
89 useStaticCredentials: false
90 username: ""
91 password: ""
92 game:
93 roofsEnabled: true
94 freeTeleports: false
95 debugContext: false
96 EOF
97
98 # Launchers (matches the previous CI's behavior)
99 - echo '#!/bin/sh' > dist/run.sh
100 - echo 'cd "$(dirname "$0")" && java -jar 377.jar' >> dist/run.sh
101 - chmod +x dist/run.sh
102 - cp dist/run.sh dist/run.command
103 - chmod +x dist/run.command
104 - printf '@echo off\r\ncd /d "%%~dp0"\r\njava -jar 377.jar\r\npause\r\n' > dist/run.bat
105
106 # Bundle
107 - cd dist && zip -r "../${ZIP_NAME}" ${JAR_NAME} config/ run.sh run.command run.bat
108 artifacts:
109 paths:
110 - "hla-${REGION}-client.zip"
111 expire_in: 90 days
112 cache:
113 key:
114 files:
115 - build.gradle
116 paths:
117 - .gradle/
118
119publish:
120 stage: publish
121 image: alpine:latest
122 tags:
123 - docker
124 parallel:
125 matrix:
126 - REGION: "us-east"
127 - REGION: "us-west"
128 # Both build and publish use identical parallel:matrix definitions, so GitLab
129 # auto-pairs them. Listing `job: build` without an explicit matrix downloads
130 # all parallel build artifacts (a few MB of waste — negligible). Each publish
131 # job uses its own REGION to pick its zip from the bundle.
132 needs:
133 - job: build
134 artifacts: true
135 rules:
136 - if: $CI_COMMIT_BRANCH == "main"
137 before_script:
138 - apk add --no-cache curl jq
139 # GitLab generic packages append on duplicate uploads (every push leaves an
140 # extra copy in the registry). Workflow: find any existing files in
141 # ${PACKAGE_NAME}/latest matching this region's zip name, DELETE them, then
142 # upload fresh. Each parallel job only touches its own ZIP_NAME so the two
143 # regions don't race.
144 script:
145 - export ZIP_NAME="hla-${REGION}-client.zip"
146 - echo "Publishing ${ZIP_NAME} @ ${CI_COMMIT_SHORT_SHA}"
147
148 # Find package id for hla-client/latest (empty string if first publish)
149 - |
150 PACKAGE_ID=$(curl --silent --fail --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
151 "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages?package_name=${PACKAGE_NAME}&package_version=latest" \
152 | jq -r '.[0].id // empty')
153 echo "Existing package id: '${PACKAGE_ID:-<none>}'"
154
155 # Delete prior copies of THIS zip name only (preserves other regions' zips)
156 - |
157 if [ -n "${PACKAGE_ID}" ]; then
158 FILE_IDS=$(curl --silent --fail --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
159 "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/${PACKAGE_ID}/package_files" \
160 | jq -r --arg name "${ZIP_NAME}" '.[] | select(.file_name == $name) | .id')
161 for fid in ${FILE_IDS}; do
162 echo "Deleting stale file id ${fid} (${ZIP_NAME})"
163 curl --silent --fail --request DELETE \
164 --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
165 "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/${PACKAGE_ID}/package_files/${fid}"
166 done
167 fi
168
169 # Upload the new zip
170 - |
171 curl --fail --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
172 --upload-file "${ZIP_NAME}" \
173 "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/latest/${ZIP_NAME}"