- Katılım
- 13 Eyl 2016
- Konular
- 3
- Mesajlar
- 11
- Online süresi
- 1d 2h
- Reaksiyon Skoru
- 7
- Altın Konu
- 0
- TM Yaşı
- 9 Yıl 9 Ay 6 Gün
- Başarım Puanı
- 58
- MmoLira
- 555
- DevLira
- 3
HERAKLES Otomatik Avlı kalıcı sunucu. 19 Haziran'da açılıyor. Atius & Wizard güvencesiyle hemen kayıt ol, ön kayıt ödülleri aktif. HEMEN TIKLA!
I hope you’re not mad at me, I played around a bit with admin.sh. I’ll be back shortly with an update here on the forumYes the command speeds up the process according to the number of CPU cores, thx![]()

here is admin.sh
[CODE title="admin.sh update"]#!/bin/sh
# ╔══════════════════════════════════════════════════════════╗
# Anka2 - Professional Server Manager v5.0
# Created by Best Studio | Optimized by CodexRO
# FreeBSD 14 | 2026
# ─────────────────────────────────────────────────────────
# Features: Monitor | Security | DB | Auto | Discord | GM
# ╚══════════════════════════════════════════════════════════╝
# ── Environment ────────────────────────────────────────────
export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat
[ ! -S /tmp/mysql.sock ] && ln -sf /var/run/mysql/mysql.sock /tmp/mysql.sock 2>/dev/null
# ── Colors ─────────────────────────────────────────────────
R="\033[0;31m" G="\033[0;32m" Y="\033[1;33m" C="\033[0;36m"
W="\033[1;37m" DG="\033[0;90m" B="\033[1m" RES="\033[0m"
BG="\033[0;34m" MG="\033[0;35m"
# ── Configuration ──────────────────────────────────────────
SFROOT="/usr/home"
LOGDIR="$SFROOT/start_logs"
BACKUP_DIR="/usr/anka2_backup"
ADMIN_LOG="/var/log/anka2_admin.log"
CRASH_LOG="/var/log/anka2_crashes.log"
CHANGELOG_FILE="/var/log/anka2_changelog.log"
CHECKSUM_FILE="/usr/local/etc/anka2_checksums.md5"
DISCORD_CFG="/usr/local/etc/anka2_discord.conf"
VERSIONS_DIR="/usr/anka2_versions"
SRC_DIR="/usr/src/Server"
BIN_DIR="$SFROOT/share/bin"
WATCHDOG_PID="/tmp/anka2_wd.pid"
MONITOR_PID="/tmp/anka2_mon.pid"
# ── Base Functions ─────────────────────────────────────────
line() { printf "${DG} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RES}\n"; }
line2() { printf "${C} ══════════════════════════════════════════════════${RES}\n"; }
ok() { printf "${G} ✓ ${RES}%s\n" "$1"; _log "OK" "$1"; }
err() { printf "${R} ✗ ${RES}%s\n" "$1"; _log "ERROR" "$1"; }
info() { printf "${C} › ${RES}%s\n" "$1"; }
warn() { printf "${Y} ⚠ ${RES}%s\n" "$1"; _log "WARNING" "$1"; }
title() { printf "\n${B}${W} ▌%s${RES}\n" "$1"; line; }
_log() { printf "[%s] [%s] %s\n" "$(date '+%d.%m.%Y %H:%M:%S')" "$1" "$2" >> "$ADMIN_LOG" 2>/dev/null; }
discord() { W=$(cat "$DISCORD_CFG" 2>/dev/null); [ -n "$W" ] && fetch -qo /dev/null --post-data="{\"content\":\"$1\"}" "$W" 2>/dev/null; }
mysql_bin() { [ -x /usr/local/bin/mariadb ] && echo "/usr/local/bin/mariadb" || echo "/usr/local/bin/mysql"; }
changelog_auto() { printf "[%s] [%s] %s\n" "$(date '+%d.%m.%Y %H:%M:%S')" "$1" "$2" >> "$CHANGELOG_FILE" 2>/dev/null; }
check_mysql() {
sockstat -l 2>/dev/null | grep -q "mysql.sock" || service mysql-server start 2>/dev/null
[ ! -S /tmp/mysql.sock ] && ln -sf /var/run/mysql/mysql.sock /tmp/mysql.sock 2>/dev/null
}
# Find PID by process working directory
pid_by_dir() {
for _p in $(pgrep -x game 2>/dev/null); do
_c=$(procstat -f "$_p" 2>/dev/null | grep " cwd " | awk '{print $NF}')
[ "$_c" = "$1" ] && echo "$_p" && return
done
}
# Display a process with RAM and CPU info
show_proc() {
_lbl="$1"; _pid="$2"
if [ -n "$_pid" ] && kill -0 "$_pid" 2>/dev/null; then
_ram=$(ps -o rss= -p "$_pid" 2>/dev/null | awk '{printf "%.0f",$1/1024}')
_cpu=$(ps -o pcpu= -p "$_pid" 2>/dev/null | xargs)
printf " ${G}●${RES} %-22s ${DG}PID:${RES}${W}%-7s${RES} ${DG}RAM:${RES}${Y}%-8s${RES} ${DG}CPU:${RES}${C}%s%%${RES}\n" \
"$_lbl" "$_pid" "${_ram}MB" "$_cpu"
else
printf " ${R}○${RES} %-22s ${DG}stopped${RES}\n" "$_lbl"
fi
}
# ══════════════════════════════════════════════════
# MAIN MENU
# ══════════════════════════════════════════════════
show_menu() {
clear
printf "\n${R}"
printf " █████╗ ███╗ ██╗██╗ ██╗ █████╗ ██████╗ \n"
printf " ██╔══██╗████╗ ██║██║ ██╔╝██╔══██╗╚════██╗\n"
printf " ███████║██╔██╗ ██║█████╔╝ ███████║ █████╔╝\n"
printf " ██╔══██║██║╚██╗██║██╔═██╗ ██╔══██║██╔═══╝ \n"
printf " ██║ ██║██║ ╚████║██║ ██╗██║ ██║███████╗\n"
printf " ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝\n"
printf "${RES}"
line2
printf "${Y} ⚔ MT2 PRIVATE SERVER v5.0 ⚔${RES}\n"
printf "${DG} Created by Best Studio | Optimized by CodexRO${RES}\n"
line2
# Status bar
_db=$(sockstat -l 2>/dev/null | grep -c "mysql.sock")
_pr=$(ps ax 2>/dev/null | grep -cE " (game|auth|db)( |$)|game99")
_wd=$([ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null && echo "${G}WD:ON${RES}" || echo "${DG}WD:OFF${RES}")
_mn=$([ -f "$MONITOR_PID" ] && kill -0 $(cat "$MONITOR_PID") 2>/dev/null && echo "${G}MON:ON${RES}" || echo "${DG}MON:OFF${RES}")
[ "$_db" -gt 0 ] && _dbt="${G}DB:ON${RES}" || _dbt="${R}DB:OFF${RES}"
[ "$_pr" -gt 0 ] && _srt="${G}SRV:${_pr}proc${RES}" || _srt="${R}SRV:OFF${RES}"
printf "\n %b │ %b │ %b │ %b │ ${DG}%s${RES}\n\n" "$_dbt" "$_srt" "$_wd" "$_mn" "$(date '+%d.%m.%Y %H:%M:%S')"
printf "${B}${DG} ┌─ SERVER ──────────────────────────────────────┐${RES}\n"
printf " ${G}1${RES} Start ${G}2${RES} Stop ${G}3${RES} Restart ${G}4${RES} Status ${G}26${RES} Health\n"
printf " ${G}27${RES} Rolling Restart\n"
printf "${B}${DG} ├─ MONITORING ──────────────────────────────────┤${RES}\n"
printf " ${C}5${RES} Dashboard ${C}6${RES} RAM Monitor ${C}7${RES} Statistics ${C}8${RES} Ports\n"
printf " ${C}9${RES} Logs ${C}10${RES} Admin Log\n"
printf "${B}${DG} ├─ AUTOMATION ──────────────────────────────────┤${RES}\n"
printf " ${Y}11${RES} Watchdog ${Y}12${RES} Scheduler ${Y}13${RES} Discord\n"
printf "${B}${DG} ├─ DATABASE ────────────────────────────────────┤${RES}\n"
printf " ${C}14${RES} DB Monitor ${C}15${RES} GM Management\n"
printf "${B}${DG} ├─ COMPILE & VERSIONS ──────────────────────────┤${RES}\n"
printf " ${Y}16${RES} Compile ${Y}17${RES} Update Binaries ${Y}18${RES} Versioning\n"
printf "${B}${DG} ├─ SECURITY ────────────────────────────────────┤${RES}\n"
printf " ${R}19${RES} DDoS & Audit ${R}30${RES} Binary Integrity\n"
printf "${B}${DG} ├─ UTILITIES ───────────────────────────────────┤${RES}\n"
printf " ${DG}20${RES} Clean Logs ${DG}21${RES} Quests ${DG}22${RES} Backup ${DG}25${RES} Report\n"
printf " ${DG}23${RES} Start DB ${DG}24${RES} Stop DB ${DG}28${RES} Changelog ${DG}29${RES} Sysctl\n"
printf "${B}${DG} └───────────────────────────────────────────────┘${RES}\n"
printf " ${R}0${RES} Exit\n\n"
printf "${B}${C} Choice › ${RES}"
}
# ══════════════════════════════════════════════════
# SERVER: START / STOP / STATUS
# ══════════════════════════════════════════════════
start_server() {
title "Start Anka2 Server"
printf " ${B}${G}How many channels? (1-4): ${RES}"; read rch
case "$rch" in 1|2|3|4) ;; *) err "Invalid number."; return 1 ;; esac
# Quick health check before start
info "Running pre-start check..."
check_mysql
[ ! -f "$BIN_DIR/game" ] && err "Game binary missing!" && return 1
[ ! -f "$BIN_DIR/db" ] && err "DB binary missing!" && return 1
ok "Pre-check OK. Starting..."
mkdir -p "$LOGDIR"
_log "INFO" "Server start - $rch channels"
discord "
**Anka2** Server started - $rch channels - $(date '+%d.%m.%Y %H:%M')"# DB
line; info "Starting DB..."
cd "$SFROOT/main/db/"
LOGDB="$LOGDIR/db.log"; : > "$LOGDB"
./vrunner --daemon --pid-path=pid --file=db >> "$LOGDB" 2>&1 &
sleep 2; cat "$LOGDB"; echo ""
# Channels
i1=1
while [ $i1 -le $rch ]; do
for i2 in 1 2 3; do
line; info "Starting Channel${i1}/core${i2}..."
cd "$SFROOT/main/channels/channel${i1}/core${i2}/"
LOGCH="$LOGDIR/ch${i1}_c${i2}.log"; : > "$LOGCH"
./vrunner --daemon --pid-path=pid --file=game >> "$LOGCH" 2>&1 &
sleep 1; cat "$LOGCH"; echo ""
done
i1=$(( i1 + 1 ))
done
# Channel99
line; info "Starting Channel99..."
cd "$SFROOT/main/channels/channel99/"
LOG99="$LOGDIR/ch99.log"; : > "$LOG99"
./vrunner --daemon --pid-path=pid --file=game99 >> "$LOG99" 2>&1 &
sleep 1; cat "$LOG99"; echo ""
# Auth
line; info "Starting Auth..."
cd "$SFROOT/main/auth/"
LOGAUTH="$LOGDIR/auth.log"; : > "$LOGAUTH"
./vrunner --daemon --pid-path=pid --file=auth >> "$LOGAUTH" 2>&1 &
sleep 1; cat "$LOGAUTH"; echo ""
line; ok "Server started! Logs: $LOGDIR"
}
stop_server() {
title "Stop Anka2 Server"
_log "INFO" "Server stop"
discord "
**Anka2** Server stopped - $(date '+%d.%m.%Y %H:%M')"pkill -f "file=auth" 2>/dev/null && ok "Auth stopped." || warn "Auth was not running."
sleep 1
pkill -f "file=game" 2>/dev/null && ok "Channels stopped." || warn "Channels were not running."
pkill -f "file=game99" 2>/dev/null; sleep 1
pkill -f "file=db" 2>/dev/null && ok "DB stopped." || warn "DB was not running."
sleep 1
pkill -x game 2>/dev/null; pkill -x db 2>/dev/null
pkill -x auth 2>/dev/null; pkill -x vrunner 2>/dev/null
line; ok "Server fully stopped."
}
server_status() {
title "Anka2 Process Status"
# DB
show_proc "db" "$(pgrep -x db 2>/dev/null)"
# Auth
_ap=""
for _p in $(ps ax 2>/dev/null | grep -E " auth" | grep -v grep | awk '{print $1}'); do
_c=$(procstat -f "$_p" 2>/dev/null | grep " cwd " | awk '{print $NF}')
[ "$_c" = "$SFROOT/main/auth" ] && _ap="$_p" && break
done
show_proc "auth" "$_ap"
# Channel99
show_proc "channel99" "$(ps ax 2>/dev/null | grep 'game99' | grep -v grep | awk '{print $1}' | head -1)"
# Channels
for i1 in 1 2 3 4; do
for i2 in 1 2 3; do
show_proc "ch${i1}/core${i2}" "$(pid_by_dir "$SFROOT/main/channels/channel${i1}/core${i2}")"
done
done
line
# Services
_dbm=$(sockstat -l 2>/dev/null | grep -c "mysql.sock")
[ "$_dbm" -gt 0 ] && printf " ${G}●${RES} MariaDB active\n" || printf " ${R}○${RES} MariaDB stopped\n"
_wd=$([ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null && echo "${G}active${RES}" || echo "${R}stopped${RES}")
printf " ${C}›${RES} Watchdog: %b\n" "$_wd"
# Stats
CON=$(netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep -c ESTABLISHED)
RAM=$(ps ax 2>/dev/null | grep -E " (game|auth|db)( |$)" | awk '{s+=$5}END{printf "%.0f",s/1024}')
printf " ${C}›${RES} Players online : ${W}%s${RES}\n" "$CON"
printf " ${C}›${RES} Total MT2 RAM : ${W}%sMB${RES}\n" "$RAM"
line
}
# ══════════════════════════════════════════════════
# HEALTH CHECK
# ══════════════════════════════════════════════════
health_check() {
title "Pre-Start Health Check"
E=0
# Disk
PCT=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
[ "$PCT" -gt 90 ] 2>/dev/null && { err "Disk full: ${PCT}%!"; E=$((E+1)); } || ok "Disk: ${PCT}% used"
# MariaDB
check_mysql
sockstat -l 2>/dev/null | grep -q "mysql.sock" && ok "MariaDB responding" || { err "MariaDB not responding!"; E=$((E+1)); }
# Socket
[ -S /tmp/mysql.sock ] && ok "MySQL socket OK" || { err "MySQL socket missing!"; E=$((E+1)); }
# Binaries
for BIN in game db auth; do
[ -f "$BIN_DIR/$BIN" ] && [ -x "$BIN_DIR/$BIN" ] \
&& ok "Binary $BIN OK" || { err "Binary $BIN MISSING/not executable!"; E=$((E+1)); }
done
# Free ports
for PORT in 30001 30101 30201; do
sockstat -l 2>/dev/null | grep -q ":$PORT " \
&& { warn "Port $PORT in use! Server already running?"; E=$((E+1)); } \
|| ok "Port $PORT free"
done
# RAM
FREE=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
[ "$FREE" -lt 512 ] 2>/dev/null && { err "Insufficient free RAM: ${FREE}MB"; E=$((E+1)); } || ok "Free RAM: ${FREE}MB"
# LD_32
[ -n "$LD_32_LIBRARY_PATH" ] && ok "LD_32_LIBRARY_PATH set" || { err "LD_32_LIBRARY_PATH missing!"; E=$((E+1)); }
line
[ "$E" -eq 0 ] && { ok "Health check PASSED!"; discord "
Anka2: Health check OK"; } \|| { err "Health check FAILED - $E issue(s)!"; discord "
Anka2: Health check failed - $E issue(s)!"; }_log "INFO" "Health check: $E errors"
line
}
# ══════════════════════════════════════════════════
# ROLLING RESTART
# ══════════════════════════════════════════════════
rolling_restart() {
title "Rolling Restart"
warn "Channels will be restarted one by one without full downtime."
printf " How many active channels (1-4): "; read RCH; RCH="${RCH:-1}"
printf " Confirm? (y/n): "; read r; [ "$r" != "y" ] && [ "$r" != "Y" ] && return
_log "INFO" "Rolling restart $RCH channels"
discord "
**Anka2** Rolling restart initiated..."i1=1
while [ $i1 -le $RCH ]; do
line; info "Rolling restart Channel $i1..."
for i2 in 1 2 3; do
_p=$(pid_by_dir "$SFROOT/main/channels/channel${i1}/core${i2}")
[ -n "$_p" ] && { kill -SIGTERM "$_p" 2>/dev/null; sleep 2; kill -0 "$_p" 2>/dev/null && kill -9 "$_p" 2>/dev/null; ok "ch${i1}/core${i2} stopped."; }
done
sleep 2
for i2 in 1 2 3; do
info "Restarting ch${i1}/core${i2}..."
cd "$SFROOT/main/channels/channel${i1}/core${i2}/" 2>/dev/null || continue
LOGCH="$LOGDIR/ch${i1}_c${i2}.log"; : > "$LOGCH"
./vrunner --daemon --pid-path=pid --file=game >> "$LOGCH" 2>&1 &
sleep 2; cat "$LOGCH"; ok "ch${i1}/core${i2} restarted."
done
ok "Channel $i1 rolling restart OK."
discord "
Anka2: Channel $i1 OK"[ $i1 -lt $RCH ] && sleep 5
i1=$(( i1 + 1 ))
done
line; ok "Rolling restart complete!"; discord "
Anka2: Rolling restart complete!"}
# ══════════════════════════════════════════════════
# LIVE DASHBOARD
# ══════════════════════════════════════════════════
live_dashboard() {
info "Press CTRL+C to exit..."; sleep 1
while true; do
clear
printf "${R} ╔══════════════════════════════════════════════════╗\n"
printf " ║ Anka2 › Live Dashboard › %s ║\n" "$(date '+%H:%M:%S')"
printf " ╚══════════════════════════════════════════════════╝${RES}\n\n"
MT=$(sysctl -n hw.physmem | awk '{printf "%.0f",$1/1024/1024}')
MU=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
CON=$(netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep -c ESTABLISHED)
LOAD=$(uptime | awk -F'load averages:' '{print $2}' | xargs)
printf " ${DG}RAM:${RES} ${Y}%sMB${RES}/${G}%sMB${RES} ${DG}Load:${RES} ${C}%s${RES} ${DG}Online:${RES} ${G}%s${RES}\n\n" "$MU" "$MT" "$LOAD" "$CON"
# RAM Bar
BAR=$(( MU * 40 / MT )); [ $BAR -gt 40 ] && BAR=40
printf " RAM ["; i=0
while [ $i -lt 40 ]; do [ $i -lt $BAR ] && printf "${G}█${RES}" || printf "${DG}░${RES}"; i=$((i+1)); done
printf "] %s%%\n\n" "$(( MU * 100 / MT ))"
line
printf "${B}${W} ACTIVE PROCESSES${RES}\n"
_tot=0
for _p in $(pgrep -x game 2>/dev/null) $(pgrep -x db 2>/dev/null); do
_cw=$(procstat -f "$_p" 2>/dev/null | grep " cwd " | awk '{print $NF}' | sed "s|$SFROOT/main/||")
_r=$(ps -o rss= -p "$_p" 2>/dev/null | awk '{printf "%.0f",$1/1024}')
_c=$(ps -o pcpu= -p "$_p" 2>/dev/null | xargs)
_b=$(( _r * 15 / 400 )); [ $_b -gt 15 ] && _b=15
printf " ${G}●${RES} %-22s ${Y}%-6sMB${RES} ${C}%-6s%%${RES} [" "${_cw}" "$_r" "$_c"
i=0; while [ $i -lt 15 ]; do [ $i -lt $_b ] && printf "${G}█${RES}" || printf "${DG}░${RES}"; i=$((i+1)); done
printf "]\n"
_tot=$(( _tot + _r ))
done
line
printf " ${DG}Total MT2 RAM:${RES} ${Y}%sMB${RES} ${DG}Refresh 3s › CTRL+C stop${RES}\n" "$_tot"
sleep 3
done
}
# ══════════════════════════════════════════════════
# RAM MONITOR / MEMORY LEAK
# ══════════════════════════════════════════════════
ram_monitor() {
title "RAM Monitor & Memory Leak Detection"
printf " ${G}1${RES}) Start ${G}2${RES}) Stop ${G}3${RES}) Status & alerts ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
[ -f "$MONITOR_PID" ] && kill -0 $(cat "$MONITOR_PID") 2>/dev/null && warn "Monitor already active." && return
printf " RAM threshold MB (default 800): "; read PRAG; PRAG="${PRAG:-800}"
printf " Interval sec (default 120): "; read INT; INT="${INT:-120}"
WH=$(cat "$DISCORD_CFG" 2>/dev/null)
cat > /tmp/anka2_mon.sh << SCR
#!/bin/sh
export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat
while true; do
for P in \$(pgrep -x game 2>/dev/null); do
R=\$(ps -o rss= -p "\$P" 2>/dev/null | awk '{printf "%.0f",\$1/1024}')
C=\$(procstat -f "\$P" 2>/dev/null | grep " cwd " | awk '{print \$NF}')
if [ "\$R" -gt "$PRAG" ] 2>/dev/null; then
echo "[\$(date '+%d.%m.%Y %H:%M:%S')] [ALERT RAM] \$C - \${R}MB > ${PRAG}MB" >> "$ADMIN_LOG"
[ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"
**Anka2 RAM Alert:** \$C = \${R}MB (threshold: ${PRAG}MB)\"}" "$WH" 2>/dev/nullfi
done
sleep $INT
done
SCR
chmod +x /tmp/anka2_mon.sh
sh /tmp/anka2_mon.sh & echo $! > "$MONITOR_PID"
ok "RAM monitor started (threshold:${PRAG}MB interval:${INT}s)"
;;
2)
[ -f "$MONITOR_PID" ] && kill $(cat "$MONITOR_PID") 2>/dev/null && rm -f "$MONITOR_PID" && ok "Monitor stopped." || warn "Not running."
;;
3)
[ -f "$MONITOR_PID" ] && kill -0 $(cat "$MONITOR_PID") 2>/dev/null \
&& ok "Monitor active PID:$(cat $MONITOR_PID)" || warn "Monitor stopped."
info "Last RAM alerts:"
grep "ALERT RAM" "$ADMIN_LOG" 2>/dev/null | tail -10 | while read L; do printf " ${Y}%s${RES}\n" "$L"; done
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# PLAYER STATISTICS
# ══════════════════════════════════════════════════
statistics() {
title "Player Statistics"
printf " ${B}${C}MariaDB root password: ${RES}"; read RP
M=$(mysql_bin)
printf " ${DG}Total accounts :${RES} ${W}%s${RES}\n" "$($M -u root -p"$RP" -se 'SELECT COUNT(*) FROM account.account;' 2>/dev/null || echo N/A)"
printf " ${DG}Total characters:${RES} ${W}%s${RES}\n" "$($M -u root -p"$RP" -se 'SELECT COUNT(*) FROM player.player;' 2>/dev/null || echo N/A)"
printf " ${DG}Online now :${RES} ${W}%s${RES}\n" "$(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED)"
echo ""; printf " ${Y}Last 5 accounts created:${RES}\n"
$M -u root -p"$RP" -se "SELECT login,create_time FROM account.account ORDER BY create_time DESC LIMIT 5;" 2>/dev/null \
| while read L; do printf " ${DG}→ %s${RES}\n" "$L"; done
line
}
# ══════════════════════════════════════════════════
# PORT CHECK
# ══════════════════════════════════════════════════
check_ports() {
title "Metin2 Port Check"
for PORT in 30001 30002 30003 30004 30101 30102 30103 30104 30105 30106 \
30201 30202 30203 30204 30205 30206 30301 30302 30303 30304 30305 30306 \
30401 30402 30403 30404 30405 30406; do
sockstat -l 2>/dev/null | grep -q ":$PORT " \
&& printf " ${G}● Port %-6s active${RES}\n" "$PORT" \
|| printf " ${R}○ Port %-6s closed${RES}\n" "$PORT"
done
line
sockstat -l 2>/dev/null | grep -q ":3306 " \
&& printf " ${G}● MariaDB 3306 active${RES}\n" \
|| printf " ${R}○ MariaDB 3306 closed${RES}\n"
printf " ${Y}› SSH port: %s${RES}\n" "$(grep '^Port' /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')"
line
}
# ══════════════════════════════════════════════════
# LOG VIEWER
# ══════════════════════════════════════════════════
view_logs() {
title "Log Viewer"
printf " ${G}1${RES}) Auth ${G}2${RES}) Ch99 ${G}3$#!/bin1 ${G}4${RES}) Ch2 ${G}5${RES}) Ch3 ${G}6${RES}) Ch4\n"
printf " ${Y}s${RES}) Search all ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1) F="$SFROOT/main/auth/syserr" ;;
2) F="$SFROOT/main/channels/channel99/syserr" ;;
3) F="$SFROOT/main/channels/channel1/core1/syserr" ;;
4) F="$SFROOT/main/channels/channel2/core1/syserr" ;;
5) F="$SFROOT/main/channels/channel3/core1/syserr" ;;
6) F="$SFROOT/main/channels/channel4/core1/syserr" ;;
s|S)
printf " Search term: "; read T
find "$SFROOT/main" -name "syserr" 2>/dev/null | while read f; do
C=$(grep -c "$T" "$f" 2>/dev/null)
[ "$C" -gt 0 ] && printf " ${Y}%s: %s result(s)${RES}\n" "$f" "$C" && grep "$T" "$f" | tail -3
done; return ;;
0) return ;;
esac
[ -f "$F" ] && { line; tail -30 "$F"; line; } || err "Log not found: $F"
}
# ══════════════════════════════════════════════════
# ADVANCED WATCHDOG
# ══════════════════════════════════════════════════
watchdog() {
title "Advanced Watchdog"
printf " ${G}1${RES}) Start ${G}2${RES}) Stop ${G}3${RES}) Status & crashes ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
[ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null && warn "Watchdog already active." && return
printf " Interval sec (default 30): "; read INT; INT="${INT:-30}"
printf " Max restarts in 5min (default 3): "; read MR; MR="${MR:-3}"
WH=$(cat "$DISCORD_CFG" 2>/dev/null)
cat > /tmp/anka2_wd_script.sh << WD
#!/bin/sh
export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat
CRASH=0; LAST=\$(date +%s)
while true; do
NOW=\$(date +%s); [ \$(( NOW - LAST )) -gt 300 ] && CRASH=0 && LAST=\$NOW
for P in db auth; do
if ! ps ax 2>/dev/null | grep -qE " \$P( |\$)"; then
CRASH=\$(( CRASH + 1 ))
TS=\$(date '+%d.%m.%Y %H:%M:%S')
echo "[\$TS] CRASH: \$P crashed (#\$CRASH)" >> "$CRASH_LOG"
echo "[\$TS] [WATCHDOG] \$P crashed" >> "$ADMIN_LOG"
if [ "\$CRASH" -le "$MR" ]; then
[ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"
**Anka2:** \$P crashed! Auto-restart #\$CRASH\"}" "$WH" 2>/dev/nullelse
[ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"
**Anka2 ALARM:** \$P crashed \$CRASH times! Manual intervention required!\"}" "$WH" 2>/dev/nullfi
fi
done
GC=\$(pgrep -x game 2>/dev/null | wc -l | xargs)
[ "\$GC" -eq 0 ] && { echo "[\$(date '+%d.%m.%Y %H:%M:%S')] CRASH: All game processes crashed" >> "$CRASH_LOG"; [ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"
**Anka2:** All channels crashed!\"}" "$WH" 2>/dev/null; }sleep $INT
done
WD
chmod +x /tmp/anka2_wd_script.sh
sh /tmp/anka2_wd_script.sh & echo $! > "$WATCHDOG_PID"
ok "Watchdog started PID:$(cat $WATCHDOG_PID) interval:${INT}s maxRestart:${MR}"
;;
2)
[ -f "$WATCHDOG_PID" ] && kill $(cat "$WATCHDOG_PID") 2>/dev/null && rm -f "$WATCHDOG_PID" && ok "Watchdog stopped." || warn "Not running."
;;
3)
[ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null \
&& ok "Watchdog active PID:$(cat $WATCHDOG_PID)" || warn "Watchdog stopped."
info "Last crashes:"
[ -f "$CRASH_LOG" ] && tail -15 "$CRASH_LOG" | while read L; do printf " ${R}%s${RES}\n" "$L"; done || info "No crashes recorded."
printf " ${C}Total crashes: ${W}%s${RES}\n" "$(wc -l < "$CRASH_LOG" 2>/dev/null || echo 0)"
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# SCHEDULER
# ══════════════════════════════════════════════════
scheduler() {
title "Automatic Scheduler"
printf " ${G}1${RES}) Daily restart ${G}2${RES}) Daily backup ${G}3${RES}) Log cleanup\n"
printf " ${G}4${RES}) Show tasks ${G}5${RES}) Delete task ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
printf " Restart hour (0-23, default 6): "; read O; O="${O:-6}"
( crontab -l 2>/dev/null | grep -v "anka2_restart"
echo "# anka2_restart"
echo "0 $O * * * export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat; pkill -f 'file=auth'; sleep 1; pkill -f 'file=game'; pkill -f 'file=game99'; sleep 1; pkill -f 'file=db'; echo '[SCHEDULER] Restart' >> $ADMIN_LOG"
) | crontab -
ok "Daily restart at $O:00"
;;
2)
printf " Backup hour (0-23, default 3): "; read O; O="${O:-3}"
( crontab -l 2>/dev/null | grep -v "anka2_backup"
echo "# anka2_backup"
echo "0 $O * * * mkdir -p $BACKUP_DIR/\$(date +%d.%m.%Y) && tar czf $BACKUP_DIR/\$(date +%d.%m.%Y)/mysql.tar.gz /var/db/mysql 2>/dev/null && echo '[SCHEDULER] DB Backup' >> $ADMIN_LOG"
) | crontab -
ok "Daily backup at $O:00"
;;
3)
printf " Every N days (default 7): "; read Z; Z="${Z:-7}"
( crontab -l 2>/dev/null | grep -v "anka2_clean"
echo "# anka2_clean"
echo "0 4 */$Z * * find $SFROOT/main -name 'syserr' -size +10M -exec truncate -s 0 {} \; && echo '[SCHEDULER] Logs cleaned' >> $ADMIN_LOG"
) | crontab -
ok "Log cleanup every $Z days"
;;
4)
line; crontab -l 2>/dev/null | grep -E "# anka2|anka2_" | while read L; do printf " ${C}%s${RES}\n" "$L"; done; line
;;
5)
printf " Task (restart/backup/clean/hourly/all): "; read T
( crontab -l 2>/dev/null | grep -v "anka2#!/bincrontab - && ok "Task $T removed."
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# DISCORD
# ══════════════════════════════════════════════════
configure_discord() {
title "Discord Webhook"
printf " ${G}1${RES}) Set URL ${G}2${RES}) Test ${G}3${RES}) Hourly report ${G}4${RES}) Manual report ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1) printf " Webhook URL: "; read U; echo "$U" > "$DISCORD_CFG"; ok "Webhook saved." ;;
2)
WH=$(cat "$DISCORD_CFG" 2>/dev/null); [ -z "$WH" ] && err "Not configured!" && return
#\$CRASH\#\$CRASH\l --post-data='{"content":"
**Anka2:** Webhook working!"}' "$WH" 2>/dev/null && ok "Test sent!" || err "Error!";;
3)
WH=$(cat "$DISCORD_CFG" 2>/dev/null); [ -z "$WH" ] && err "Not configured!" && return
( crontab -l 2>/dev/null | grep -v "anka2_hourly"
echo "# anka2_hourly"
echo "0 * * * * CON=\$(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED); fetch -qo /dev/null --post-data=\"{\\\"content\\\":\\\"
**Anka2** Online: \$CON | \$(date '+%H:%M')\\\"}\" \"$WH\" 2>/dev/null") | crontab -
ok "Hourly report configured."
;;
4)
WH=$(cat "$DISCORD_CFG" 2>/dev/null); [ -z "$WH" ] && err "Not configured!" && return
CON=$(netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep -c ESTABLISHED)
MU=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
LD=$(uptime | awk -F'load averages:' '{print $2}' | xargs)
fetch -qo /dev/null --post-data="{\"content\":\"
**Anka2** $(date '+%d.%m.%Y %H:%M')\n
Online: $CON\n
RAM: ${MU}MB\n
Load: $LD\"}" "$WH" 2>/dev/null \&& ok "Report sent!" || err "Error!"
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# DB MONITOR
# ══════════════════════════════════════════════════
db_monitor() {
title "Database Monitor"
printf " ${G}1${RES}) Tables ${G}2${RES}) Duplicates ${G}3${RES}) Inactive >90d ${G}4${RES}) Console ${G}5${RES}) Password ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
[ "$opt" = "0" ] && return
printf " MariaDB root password: "; read RP
M=$(mysql_bin)
case "$opt" in
1) $M -u root -p"$RP" -e "SELECT table_schema,table_name,ROUND((data_length+index_length)/1024/1024,2) AS MB FROM information_schema.tables ORDER BY (data_length+index_length) DESC LIMIT 20;" 2>/dev/null | while read L; do printf " ${DG}%s${RES}\n" "$L"; done ;;
2) $M -u root -p"$RP" -e "SELECT name,COUNT(*) cnt FROM player.player GROUP BY name HAVING cnt>1 LIMIT 20;" 2>/dev/null | while read L; do printf " ${Y}%s${RES}\n" "$L"; done ;;
3) $M -u root -p"$RP" -e "SELECT login,last_play FROM account.account WHERE last_play < DATE_SUB(NOW(),INTERVAL 90 DAY) ORDER BY last_play LIMIT 20;" 2>/dev/null | while read L; do printf " ${DG}%s${RES}\n" "$L"; done ;;
4) $M -u root -p"$RP" 2>/dev/null ;;
5)
printf " New password: "; read NP; printf " Confirm: "; read NP2
[ "$NP" != "$NP2" ] && err "Passwords do not match!" && return
$M -u root -p"$RP" -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$NP'; ALTER USER 'root'@'%' IDENTIFIED BY '$NP'; FLUSH PRIVILEGES;" 2>/dev/null && ok "Password changed!" || err "Error!"
;;
esac; line
}
# ══════════════════════════════════════════════════
# GM MANAGEMENT
# ══════════════════════════════════════════════════
gm_management() {
title "GM Management"
printf " ${G}1${RES}) List GMs ${G}2${RES}) Add GM ${G}3${RES}) Remove GM ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
[ "$opt" = "0" ] && return
printf " MariaDB root password: "; read RP
M=$(mysql_bin)
case "$opt" in
1) $M -u root -p"$RP" -e "SELECT login,status FROM account.account WHERE status='GM' LIMIT 20;" 2>/dev/null | while read L; do printf " ${Y}%s${RES}\n" "$L"; done ;;
2) printf " Username: "; read GM; $M -u root -p"$RP" -e "UPDATE account.account SET status='GM' WHERE login='$GM';" 2>/dev/null && ok "GM added: $GM" || err "Error!"; _log "INFO" "GM added: $GM" ;;
3) printf " Username: "; read GM; $M -u root -p"$RP" -e "UPDATE account.account SET status='OK' WHERE login='$GM';" 2>/dev/null && ok "GM removed: $GM" || err "Error!"; _log "INFO" "GM removed: $GM" ;;
esac; line
}
# ══════════════════════════════════════════════════
# COMPILE SERVER
# ══════════════════════════════════════════════════
compile_server() {
title "Compile Server"
printf " ${G}1${RES}) DB ${G}2${RES}) Game ${G}3${RES}) All ${Y}4${RES}) Clean+All ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
[ ! -d "$SRC_DIR" ] && err "Source directory not found!" && return
cd "$SRC_DIR"
case "$opt" in
1) gmake db && ok "DB compiled!" || err "DB error!" ;;
2) gmake game && ok "Game compiled!" || err "Game error!" ;;
3) gmake db && gmake game && ok "Full compile done!" || err "Error!" ;;
4) gmake clean; gmake db && gmake game && ok "Clean+Compile OK!" || err "Error!" ;;
0) return ;;
esac
_log "INFO" "Compile opt:$opt"; line
}
# ══════════════════════════════════════════════════
# UPDATE BINARIES
# ══════════════════════════════════════════════════
update_binaries() {
title "Update Binaries"
warn "Server must be stopped before update!"
printf " Continue? (y/n): "; read r; [ "$r" != "y" ] && [ "$r" != "Y" ] && return
TS=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BIN_DIR/backup_$TS"
[ -f "$BIN_DIR/game" ] && cp "$BIN_DIR/game" "$BIN_DIR/backup_$TS/game.old" && ok "Game backup saved."
[ -f "$BIN_DIR/db" ] && cp "$BIN_DIR/db" "$BIN_DIR/backup_$TS/db.old" && ok "DB backup saved."
cd "$SRC_DIR" && gmake db && gmake game && ok "Binaries updated!" || err "Compile error!"
_log "INFO" "Binary update backup:$BIN_DIR/backup_$TS"; line
}
# ══════════════════════════════════════════════════
# VERSIONING
# ══════════════════════════════════════════════════
versioning() {
title "Versioning & Rollback"
printf " ${G}1${RES}) Save ${G}2${RES}) List ${G}3${RES}) Rollback ${G}4${RES}) Delete ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
mkdir -p "$VERSIONS_DIR"
case "$opt" in
1)
printf " Tag (e.g. v1.0): "; read TAG; TAG="${TAG:-$(date +%Y%m%d_%H%M%S)}"
mkdir -p "$VERSIONS_DIR/$TAG"
[ -f "$BIN_DIR/game" ] && cp "$BIN_DIR/game" "$VERSIONS_DIR/$TAG/game" && ok "game saved."
[ -f "$BIN_DIR/db" ] && cp "$BIN_DIR/db" "$VERSIONS_DIR/$TAG/db" && ok "db saved."
date '+%d.%m.%Y %H:%M:%S' > "$VERSIONS_DIR/$TAG/timestamp"
ok "Version '$TAG' saved."; _log "INFO" "Version: $TAG"
;;
2)
line; ls -lt "$VERSIONS_DIR" 2>/dev/null | grep "^d" | while read L; do
T=$(echo "$L" | awk '{print $NF}')
TS=$(cat "$VERSIONS_DIR/$T/timestamp" 2>/dev/null)
printf " ${C}%-30s${RES} %s\n" "$T" "$TS"
done; line
;;
3)
ls "$VERSIONS_DIR" 2>/dev/null; printf " Version: "; read TAG
[ ! -d "$VERSIONS_DIR/$TAG" ] && err "Not found!" && return
warn "Server will be stopped!"; printf " Confirm? (y/n): "; read r
[ "$r" != "y" ] && [ "$r" != "Y" ] && return
pkill -f "file=auth" 2>/dev/null; pkill -f "file=game" 2>/dev/null
pkill -f "file=game99" 2>/dev/null; pkill -f "file=db" 2>/dev/null; sleep 2
[ -f "$VERSIONS_DIR/$TAG/game" ] && cp "$VERSIONS_DIR/$TAG/game" "$BIN_DIR/game" && ok "game restored."
[ -f "$VERSIONS_DIR/$TAG/db" ] && cp "$VERSIONS_DIR/$TAG/db" "$BIN_DIR/db" && ok "db restored."
ok "Rollback to '$TAG' complete."; _log "INFO" "Rollback: $TAG"
;;
4)
ls "$VERSIONS_DIR" 2>/dev/null; printf " Version to delete: "; read TAG
rm -rf "$VERSIONS_DIR/$TAG" && ok "Version '$TAG' deleted."
;;
0) return ;;
esac; line
}
# ══════════════════════════════════════════════════
# DDOS PROTECTION
# ══════════════════════════════════════════════════
ddos_protection() {
title "DDoS Protection & Security"
printf " ${G}1${RES}) Rate limiting ${G}2${RES}) Blocked IPs ${G}3${RES}) Suspicious IPs ${G}4${RES}) SSH Audit ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
printf " Max connections/IP (default 10): "; read MAX; MAX="${MAX:-10}"
cat > /etc/pf.conf << PFEOF
ext_if = "vtnet0"
table <sshguard> persist
table <ddos_block> persist
set block-policy drop
block all
pass quick on lo0 all
pass out quick on \$ext_if all keep state
block in quick on \$ext_if from <sshguard>
block in quick on \$ext_if from <ddos_block>
pass in quick on \$ext_if proto tcp to port 223 keep state
pass in quick on \$ext_if proto tcp to port {30001 30002 30003 30004} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30101 30102 30103 30104 30105 30106} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30201 30202 30203 30204 30205 30206} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30301 30302 30303 30304 30305 30306} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30401 30402 30403 30404 30405 30406} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on lo0 proto tcp to port 3306 keep state
pass in quick on \$ext_if proto tcp to port 3306 keep state
PFEOF
pfctl -F all -f /etc/pf.conf 2>/dev/null && ok "Rate limiting applied ($MAX/IP)" || err "pf error!"
_log "INFO" "Rate limiting: $MAX/IP"
;;
2)
line; info "ddos_block table:"
pfctl -t ddos_block -T show 2>/dev/null | while read IP; do printf " ${R}✗ %s${RES}\n" "$IP"; done
printf "\n Clear table? (y/n): "; read r
[ "$r" = "y" ] || [ "$r" = "Y" ] && pfctl -t ddos_block -T flush 2>/dev/null && ok "Table cleared."
;;
3)
line; netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep ESTABLISHED \
| awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -15 \
| while read C IP; do
[ "$C" -gt 5 ] && printf " ${R}⚠ %-20s %s conn${RES}\n" "$IP" "$C" \
|| printf " ${G}● %-20s %s conn${RES}\n" "$IP" "$C"
done; line
;;
4)
line; info "Last 20 SSH logins:"; last -20 | head -20 | while read L; do printf " ${DG}%s${RES}\n" "$L"; done
line; info "Recent failures:"; grep "Failed\|Invalid" /var/log/auth.log 2>/dev/null | tail -10 | while read L; do printf " ${R}%s${RES}\n" "$L"; done
line
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# CLEAN LOGS
# ══════════════════════════════════════════════════
clean_logs() {
title "Clean Logs"
_clf() { rm -rf log/* start.log packet_info.txt syserr syslog stdout PTS p2p_packet_info.txt mob_count DEV_LOG.log version.txt udp_packet_info.txt usage.txt mob_data.txt pid game.core db.core; }
cd "$SFROOT/main/db/" && _clf
for i1 in 1 2 3 4; do for i2 in 1 2 3; do cd "$SFROOT/main/channels/channel${i1}/core${i2}/" 2>/dev/null && _clf; done; done
cd "$SFROOT/main/channels/channel99/" && _clf
cd "$SFROOT/main/auth/" && _clf
cd /var/db/mysql/ && rm -rf *.err *.pid
line; ok "Logs cleaned."; _log "INFO" "Logs cleaned"
}
# ══════════════════════════════════════════════════
# COMPILE QUESTS
# ══════════════════════════════════════════════════
compile_quests() {
title "Compile Quests"
cd "$SFROOT/share/locale/europe/quest/"
[ -r quest_list ] && rm -rdf object && mkdir object \
&& while read L; do ./qc $L; done < quest_list \
&& ok "Quests compiled." && _log "INFO" "Quests compiled" \
|| err "quest_list not found!"
line
}
# ══════════════════════════════════════════════════
# BACKUP
# ══════════════════════════════════════════════════
backup_server() {
title "Server Backup"
_log "INFO" "Backup initiated"
TGT="$BACKUP_DIR/$(date +%d.%m.%Y_%H.%M)"; mkdir -p "$TGT"
info "Backing up server → anka2_game.tar.gz"
tar czf "$TGT/anka2_game.tar.gz" /usr/home 2>/dev/null && ok "Server backup OK."
info "Backing up DB → anka2_mysql.tar.gz"
tar czf "$TGT/anka2_mysql.tar.gz" /var/db/mysql 2>/dev/null && ok "DB backup OK."
line; ok "Saved to: $TGT"; du -sh "$TGT"
_log "INFO" "Backup: $TGT"; line
}
# ══════════════════════════════════════════════════
# SYSTEM REPORT
# ══════════════════════════════════════════════════
system_report() {
F="/tmp/anka2_report_$(date +%Y%m%d_%H%M%S).txt"
MT=$(sysctl -n hw.physmem | awk '{printf "%.0f",$1/1024/1024}')
MU=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
{
echo "═══════════════════════════════════════════════"
echo " ANKA2 - System Report - $(date '+%d.%m.%Y %H:%M:%S')"
echo " Created by Best Studio | Optimized by CodexRO"
echo "═══════════════════════════════════════════════"
echo "Hostname : $(hostname)"
echo "IP : $(ifconfig vtnet0 2>/dev/null | grep 'inet ' | awk '{print $2}')"
echo "FreeBSD : $(freebsd-version 2>/dev/null)"
echo "CPU : $(sysctl -n hw.model) ($(sysctl -n hw.ncpu) cores)"
echo "Uptime : $(uptime)"
echo "RAM : ${MU}MB / ${MT}MB"
echo "Disk : $(df -h / | tail -1 | awk '{print $3"/"$2" ("$5")"}')"
echo "Load : $(uptime | awk -F'load averages:' '{print $2}')"
echo ""
echo "--- Processes ---"
ps ax | grep -E " (game|auth|db)( |$)|game99" | grep -v grep
echo ""
echo "--- Online: $(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED) players ---"
echo ""
echo "--- Last 20 admin actions ---"
tail -20 "$ADMIN_LOG" 2>/dev/null
echo "═══════════════════════════════════════════════"
} > "$F"
ok "Report: $F"
printf " Display? (y/n): "; read r; [ "$r" = "y" ] || [ "$r" = "Y" ] && less "$F" 2>/dev/null || cat "$F"
WH=$(cat "$DISCORD_CFG" 2>/dev/null)
if [ -n "$WH" ]; then
printf " Send to Discord? (y/n): "; read r
[ "$r" = "y" ] || [ "$r" = "Y" ] && discord "
**Anka2 Report** $(date '+%d.%m.%Y %H:%M') RAM:${MU}MB/${MT}MB Online:$(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED)" && ok "Sent!"fi
line
}
# ══════════════════════════════════════════════════
# CHANGELOG
# ══════════════════════════════════════════════════
changelog() {
title "Changelog & Notes"
printf " ${G}1${RES}) Add ${G}2${RES}) View ${G}3${RES}) Search ${G}4${RES}) Export ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
printf " Category (fix/update/config/other): "; read CAT; CAT="${CAT:-update}"
printf " Description: "; read DESC; [ -z "$DESC" ] && err "Description cannot be empty!" && return
printf "[%s] [%s] %s\n" "$(date '+%d.%m.%Y %H:%M:%S')" "$CAT" "$DESC" >> "$CHANGELOG_FILE"
ok "Entry added."
;;
2)
line
[ -f "$CHANGELOG_FILE" ] && tail -20 "$CHANGELOG_FILE" | while read L; do
echo "$L" | grep -q "\[fix\]" && printf " ${G}%s${RES}\n" "$L" && continue
echo "$L" | grep -q "\[update\]" && printf " ${C}%s${RES}\n" "$L" && continue
echo "$L" | grep -q "\[config\]" && printf " ${Y}%s${RES}\n" "$L" && continue
printf " ${DG}%s${RES}\n" "$L"
done || info "Changelog empty."
printf " ${DG}Total: %s entries${RES}\n" "$(wc -l < "$CHANGELOG_FILE" 2>/dev/null || echo 0)"
line
;;
3)
printf " Search term: "; read T
grep -i "$T" "$CHANGELOG_FILE" 2>/dev/null | while read L; do printf " ${Y}%s${RES}\n" "$L"; done
;;
4)
E="/tmp/changelog_$(date +%Y%m%d).txt"
cp "$CHANGELOG_FILE" "$E" 2>/dev/null && ok "Exported: $E" || err "Changelog empty."
;;
0) return ;;
esac; line
}
# ══════════════════════════════════════════════════
# AUTO-TUNE SYSCTL
# ══════════════════════════════════════════════════
autotune_sysctl() {
title "Auto-tune Sysctl"
MT=$(sysctl -n hw.physmem | awk '{printf "%.0f",$1/1024/1024}')
NC=$(sysctl -n hw.ncpu)
info "Detected RAM: ${MT}MB | CPU cores: $NC"
SB=$(( MT * 1024 * 64 )); [ $SB -gt 67108864 ] && SB=67108864
MF=$(( MT * 32 )); [ $MF -gt 524288 ] && MF=524288
echo ""
printf " ${C}Optimal values for ${MT}MB RAM:${RES}\n"
printf " ${DG}kern.ipc.maxsockbuf${RES} = ${Y}%s${RES}\n" "$SB"
printf " ${DG}kern.maxfiles${RES} = ${Y}%s${RES}\n" "$MF"
printf " ${DG}tcp.sendspace${RES} = ${Y}131072${RES}\n"
echo ""
printf " Apply? (y/n): "; read r; [ "$r" != "y" ] && [ "$r" != "Y" ] && return
cat > /etc/sysctl.conf << SYSCTL
# Anka2 - Auto-tune ${MT}MB RAM / ${NC} CPU - $(date '+%d.%m.%Y %H:%M:%S')
# Created by Best Studio | Optimized by CodexRO
vfs.read_max=128
kern.ipc.maxsockbuf=$SB
net.inet.ip.forwarding=1
net.inet.tcp.hostcache.expire=3900
kern.ipc.somaxconn=65535
security.bsd.see_other_uids=0
net.inet.tcp.mssdflt=1440
net.inet.tcp.nolocaltimewait=1
net.inet.tcp.sendspace=131072
net.inet.tcp.recvspace=131072
net.inet.tcp.syncookies=1
net.inet.tcp.syncache.rexmtlimit=1
net.inet.ip.portrange.randomized=1
net.inet.ip.process_options=0
kern.maxfiles=$MF
kern.maxfilesperproc=$(( MF / 2 ))
net.inet.ip.random_id=1
net.inet.ip.redirect=0
net.inet.ip.sourceroute=0
net.inet.ip.accept_sourceroute=0
net.local.stream.recvspace=32768
net.local.stream.sendspace=65535
net.inet.udp.maxdgram=57344
net.inet.icmp.bmcastecho=0
net.inet.icmp.log_redirect=1
net.inet.icmp.drop_redirect=1
net.inet.tcp.always_keepalive=0
net.inet.ip.intr_queue_maxlen=1000
net.inet.tcp.drop_synfin=1
net.inet.tcp.delayed_ack=0
net.inet.tcp.fast_finwait2_recycle=1
net.inet.tcp.icmp_may_rst=0
net.inet.tcp.msl=5000
net.inet.tcp.path_mtu_discovery=0
net.inet.tcp.rfc1323=1
net.inet.tcp.rfc3042=1
net.inet.tcp.rfc3390=1
net.inet.tcp.sack.enable=1
net.inet.udp.blackhole=1
net.inet.tcp.blackhole=2
net.inet.icmp.icmplim=2500
vm.swap_idle_enabled=1
vm.swap_idle_threshold1=15
vm.swap_idle_threshold2=10
SYSCTL
sysctl -f /etc/sysctl.conf 2>/dev/null | grep -v "^sysctl: unknown" | tail -5
ok "Sysctl optimized for ${MT}MB RAM!"
_log "INFO" "Auto-tune sysctl RAM=${MT}MB CPU=${NC}"
changelog_auto "config" "Auto-tune sysctl RAM=${MT}MB"
line
}
# ══════════════════════════════════════════════════
# BINARY INTEGRITY
# ══════════════════════════════════════════════════
binary_integrity() {
title "Binary Integrity"
printf " ${G}1${RES}) Save reference ${G}2${RES}) Verify ${G}3${RES}) Display ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
: > "$CHECKSUM_FILE"
for BIN in game db auth; do
[ -f "$BIN_DIR/$BIN" ] || { warn "$BIN not found."; continue; }
MD5=$(md5 -q "$BIN_DIR/$BIN" 2>/dev/null)
SZ=$(ls -la "$BIN_DIR/$BIN" | awk '{print $5}')
printf "%s %s %s %s\n" "$MD5" "$SZ" "$BIN_DIR/$BIN" "$(date '+%d.%m.%Y %H:%M:%S')" >> "$CHECKSUM_FILE"
ok "$BIN: $MD5"
done
ok "Checksums saved: $CHECKSUM_FILE"; _log "INFO" "Checksums saved"; changelog_auto "config" "Binary checksums saved"
;;
2)
[ ! -f "$CHECKSUM_FILE" ] && err "No reference found! Run option 1 first." && return
MOD=0
while IFS= read -r L; do
SM=$(echo "$L" | awk '{print $1}')
BP=$(echo "$L" | awk '{print $3}')
BN=$(basename "$BP")
[ ! -f "$BP" ] && { err "$BN: MISSING!"; MOD=$((MOD+1)); continue; }
CM=$(md5 -q "$BP" 2>/dev/null)
[ "$CM" = "$SM" ] && ok "$BN: OK" || {
err "$BN: MODIFIED!"
printf " ${DG}Saved : %s${RES}\n" "$SM"
printf " ${R}Current : %s${RES}\n" "$CM"
MOD=$((MOD+1))
discord "
Anka2: Binary $BN MODIFIED!"_log "ERROR" "Binary $BN modified!"
}
done < "$CHECKSUM_FILE"
line
[ "$MOD" -eq 0 ] && ok "All binaries intact!" || err "$MOD binary/binaries modified!"
;;
3)
[ -f "$CHECKSUM_FILE" ] && cat "$CHECKSUM_FILE" | while read L; do printf " ${DG}%s${RES}\n" "$L"; done || info "No reference saved."
;;
0) return ;;
esac; line
}
# ══════════════════════════════════════════════════
# MAIN LOOP
# ══════════════════════════════════════════════════
_log "INFO" "Anka2 Server Manager v5.0 started - Created by Best Studio | Optimized by CodexRO"
while true; do
show_menu; read chs; echo ""
case "$chs" in
1) start_server ;;
2) stop_server ;;
3) stop_server; sleep 2; start_server ;;
4) server_status ;;
5) live_dashboard ;;
6) ram_monitor ;;
7) statistics ;;
8) check_ports ;;
9) view_logs ;;
10) title "Admin Log"; tail -40 "$ADMIN_LOG" 2>/dev/null || warn "Log empty."; line ;;
11) watchdog ;;
12) scheduler ;;
13) configure_discord ;;
14) db_monitor ;;
15) gm_management ;;
16) compile_server ;;
17) update_binaries ;;
18) versioning ;;
19) ddos_protection ;;
20) clean_logs ;;
21) compile_quests ;;
22) backup_server ;;
23) check_mysql && service mysql-server start && ok "MariaDB started." ;;
24) service mysql-server stop && ok "MariaDB stopped." ;;
25) system_report ;;
26) health_check ;;
27) rolling_restart ;;
28) changelog ;;
29) autotune_sysctl ;;
30) binary_integrity ;;
0) _log "INFO" "Manager closed"
printf "\n${G} Goodbye! Anka2 - Best Studio | CodexRO${RES}\n\n"; exit 0 ;;
*) err "Invalid choice." ;;
esac
printf "\n${DG} ─────────────────────────────────────────────────${RES}\n"
printf "${DG} Press ENTER to continue...${RES}"; read _
done
[/CODE]
replace : 223 your port ssh login
pass in quick on \$ext_if proto tcp to port 223 keep state
Son düzenleme:
- Katılım
- 8 Kas 2019
- Konular
- 116
- Mesajlar
- 489
- Çözüm
- 39
- Online süresi
- 22d 4h
- Reaksiyon Skoru
- 29
- Altın Konu
- 0
- TM Yaşı
- 6 Yıl 7 Ay 10 Gün
- Başarım Puanı
- 120
- MmoLira
- 3,033
- DevLira
- 0
@Best Studio filesteki offline shop ve ticaret camı hangi konuda var acaba ne diye geçiyor ismi
- Katılım
- 16 Ocak 2026
- Konular
- 1
- Mesajlar
- 89
- Online süresi
- 2d 4h
- Reaksiyon Skoru
- 33
- Altın Konu
- 0
- TM Yaşı
- 4 Ay 28 Gün
- Başarım Puanı
- 18
- MmoLira
- 901
- DevLira
- 0
What's the updated things now?I hope you’re not mad at me, I played around a bit with admin.sh. I’ll be back shortly with an update here on the forum
Ekli dosyayı görüntüle 171979
here is admin.sh
[CODE title="admin.sh update"]#!/bin/sh
# ╔══════════════════════════════════════════════════════════╗
# Anka2 - Professional Server Manager v5.0
# Created by Best Studio | Optimized by CodexRO
# FreeBSD 14 | 2026
# ─────────────────────────────────────────────────────────
# Features: Monitor | Security | DB | Auto | Discord | GM
# ╚══════════════════════════════════════════════════════════╝
# ── Environment ────────────────────────────────────────────
export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat
[ ! -S /tmp/mysql.sock ] && ln -sf /var/run/mysql/mysql.sock /tmp/mysql.sock 2>/dev/null
# ── Colors ─────────────────────────────────────────────────
R="\033[0;31m" G="\033[0;32m" Y="\033[1;33m" C="\033[0;36m"
W="\033[1;37m" DG="\033[0;90m" B="\033[1m" RES="\033[0m"
BG="\033[0;34m" MG="\033[0;35m"
# ── Configuration ──────────────────────────────────────────
SFROOT="/usr/home"
LOGDIR="$SFROOT/start_logs"
BACKUP_DIR="/usr/anka2_backup"
ADMIN_LOG="/var/log/anka2_admin.log"
CRASH_LOG="/var/log/anka2_crashes.log"
CHANGELOG_FILE="/var/log/anka2_changelog.log"
CHECKSUM_FILE="/usr/local/etc/anka2_checksums.md5"
DISCORD_CFG="/usr/local/etc/anka2_discord.conf"
VERSIONS_DIR="/usr/anka2_versions"
SRC_DIR="/usr/src/Server"
BIN_DIR="$SFROOT/share/bin"
WATCHDOG_PID="/tmp/anka2_wd.pid"
MONITOR_PID="/tmp/anka2_mon.pid"
# ── Base Functions ─────────────────────────────────────────
line() { printf "${DG} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RES}\n"; }
line2() { printf "${C} ══════════════════════════════════════════════════${RES}\n"; }
ok() { printf "${G} ✓ ${RES}%s\n" "$1"; _log "OK" "$1"; }
err() { printf "${R} ✗ ${RES}%s\n" "$1"; _log "ERROR" "$1"; }
info() { printf "${C} › ${RES}%s\n" "$1"; }
warn() { printf "${Y} ⚠ ${RES}%s\n" "$1"; _log "WARNING" "$1"; }
title() { printf "\n${B}${W} ▌%s${RES}\n" "$1"; line; }
_log() { printf "[%s] [%s] %s\n" "$(date '+%d.%m.%Y %H:%M:%S')" "$1" "$2" >> "$ADMIN_LOG" 2>/dev/null; }
discord() { W=$(cat "$DISCORD_CFG" 2>/dev/null); [ -n "$W" ] && fetch -qo /dev/null --post-data="{\"content\":\"$1\"}" "$W" 2>/dev/null; }
mysql_bin() { [ -x /usr/local/bin/mariadb ] && echo "/usr/local/bin/mariadb" || echo "/usr/local/bin/mysql"; }
changelog_auto() { printf "[%s] [%s] %s\n" "$(date '+%d.%m.%Y %H:%M:%S')" "$1" "$2" >> "$CHANGELOG_FILE" 2>/dev/null; }
check_mysql() {
sockstat -l 2>/dev/null | grep -q "mysql.sock" || service mysql-server start 2>/dev/null
[ ! -S /tmp/mysql.sock ] && ln -sf /var/run/mysql/mysql.sock /tmp/mysql.sock 2>/dev/null
}
# Find PID by process working directory
pid_by_dir() {
for _p in $(pgrep -x game 2>/dev/null); do
_c=$(procstat -f "$_p" 2>/dev/null | grep " cwd " | awk '{print $NF}')
[ "$_c" = "$1" ] && echo "$_p" && return
done
}
# Display a process with RAM and CPU info
show_proc() {
_lbl="$1"; _pid="$2"
if [ -n "$_pid" ] && kill -0 "$_pid" 2>/dev/null; then
_ram=$(ps -o rss= -p "$_pid" 2>/dev/null | awk '{printf "%.0f",$1/1024}')
_cpu=$(ps -o pcpu= -p "$_pid" 2>/dev/null | xargs)
printf " ${G}●${RES} %-22s ${DG}PID:${RES}${W}%-7s${RES} ${DG}RAM:${RES}${Y}%-8s${RES} ${DG}CPU:${RES}${C}%s%%${RES}\n" \
"$_lbl" "$_pid" "${_ram}MB" "$_cpu"
else
printf " ${R}○${RES} %-22s ${DG}stopped${RES}\n" "$_lbl"
fi
}
# ══════════════════════════════════════════════════
# MAIN MENU
# ══════════════════════════════════════════════════
show_menu() {
clear
printf "\n${R}"
printf " █████╗ ███╗ ██╗██╗ ██╗ █████╗ ██████╗ \n"
printf " ██╔══██╗████╗ ██║██║ ██╔╝██╔══██╗╚════██╗\n"
printf " ███████║██╔██╗ ██║█████╔╝ ███████║ █████╔╝\n"
printf " ██╔══██║██║╚██╗██║██╔═██╗ ██╔══██║██╔═══╝ \n"
printf " ██║ ██║██║ ╚████║██║ ██╗██║ ██║███████╗\n"
printf " ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝\n"
printf "${RES}"
line2
printf "${Y} ⚔ MT2 PRIVATE SERVER v5.0 ⚔${RES}\n"
printf "${DG} Created by Best Studio | Optimized by CodexRO${RES}\n"
line2
# Status bar
_db=$(sockstat -l 2>/dev/null | grep -c "mysql.sock")
_pr=$(ps ax 2>/dev/null | grep -cE " (game|auth|db)( |$)|game99")
_wd=$([ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null && echo "${G}WD:ON${RES}" || echo "${DG}WD:OFF${RES}")
_mn=$([ -f "$MONITOR_PID" ] && kill -0 $(cat "$MONITOR_PID") 2>/dev/null && echo "${G}MON:ON${RES}" || echo "${DG}MON:OFF${RES}")
[ "$_db" -gt 0 ] && _dbt="${G}DB:ON${RES}" || _dbt="${R}DB:OFF${RES}"
[ "$_pr" -gt 0 ] && _srt="${G}SRV:${_pr}proc${RES}" || _srt="${R}SRV:OFF${RES}"
printf "\n %b │ %b │ %b │ %b │ ${DG}%s${RES}\n\n" "$_dbt" "$_srt" "$_wd" "$_mn" "$(date '+%d.%m.%Y %H:%M:%S')"
printf "${B}${DG} ┌─ SERVER ──────────────────────────────────────┐${RES}\n"
printf " ${G}1${RES} Start ${G}2${RES} Stop ${G}3${RES} Restart ${G}4${RES} Status ${G}26${RES} Health\n"
printf " ${G}27${RES} Rolling Restart\n"
printf "${B}${DG} ├─ MONITORING ──────────────────────────────────┤${RES}\n"
printf " ${C}5${RES} Dashboard ${C}6${RES} RAM Monitor ${C}7${RES} Statistics ${C}8${RES} Ports\n"
printf " ${C}9${RES} Logs ${C}10${RES} Admin Log\n"
printf "${B}${DG} ├─ AUTOMATION ──────────────────────────────────┤${RES}\n"
printf " ${Y}11${RES} Watchdog ${Y}12${RES} Scheduler ${Y}13${RES} Discord\n"
printf "${B}${DG} ├─ DATABASE ────────────────────────────────────┤${RES}\n"
printf " ${C}14${RES} DB Monitor ${C}15${RES} GM Management\n"
printf "${B}${DG} ├─ COMPILE & VERSIONS ──────────────────────────┤${RES}\n"
printf " ${Y}16${RES} Compile ${Y}17${RES} Update Binaries ${Y}18${RES} Versioning\n"
printf "${B}${DG} ├─ SECURITY ────────────────────────────────────┤${RES}\n"
printf " ${R}19${RES} DDoS & Audit ${R}30${RES} Binary Integrity\n"
printf "${B}${DG} ├─ UTILITIES ───────────────────────────────────┤${RES}\n"
printf " ${DG}20${RES} Clean Logs ${DG}21${RES} Quests ${DG}22${RES} Backup ${DG}25${RES} Report\n"
printf " ${DG}23${RES} Start DB ${DG}24${RES} Stop DB ${DG}28${RES} Changelog ${DG}29${RES} Sysctl\n"
printf "${B}${DG} └───────────────────────────────────────────────┘${RES}\n"
printf " ${R}0${RES} Exit\n\n"
printf "${B}${C} Choice › ${RES}"
}
# ══════════════════════════════════════════════════
# SERVER: START / STOP / STATUS
# ══════════════════════════════════════════════════
start_server() {
title "Start Anka2 Server"
printf " ${B}${G}How many channels? (1-4): ${RES}"; read rch
case "$rch" in 1|2|3|4) ;; *) err "Invalid number."; return 1 ;; esac
# Quick health check before start
info "Running pre-start check..."
check_mysql
[ ! -f "$BIN_DIR/game" ] && err "Game binary missing!" && return 1
[ ! -f "$BIN_DIR/db" ] && err "DB binary missing!" && return 1
ok "Pre-check OK. Starting..."
mkdir -p "$LOGDIR"
_log "INFO" "Server start - $rch channels"
discord "**Anka2** Server started - $rch channels - $(date '+%d.%m.%Y %H:%M')"
# DB
line; info "Starting DB..."
cd "$SFROOT/main/db/"
LOGDB="$LOGDIR/db.log"; : > "$LOGDB"
./vrunner --daemon --pid-path=pid --file=db >> "$LOGDB" 2>&1 &
sleep 2; cat "$LOGDB"; echo ""
# Channels
i1=1
while [ $i1 -le $rch ]; do
for i2 in 1 2 3; do
line; info "Starting Channel${i1}/core${i2}..."
cd "$SFROOT/main/channels/channel${i1}/core${i2}/"
LOGCH="$LOGDIR/ch${i1}_c${i2}.log"; : > "$LOGCH"
./vrunner --daemon --pid-path=pid --file=game >> "$LOGCH" 2>&1 &
sleep 1; cat "$LOGCH"; echo ""
done
i1=$(( i1 + 1 ))
done
# Channel99
line; info "Starting Channel99..."
cd "$SFROOT/main/channels/channel99/"
LOG99="$LOGDIR/ch99.log"; : > "$LOG99"
./vrunner --daemon --pid-path=pid --file=game99 >> "$LOG99" 2>&1 &
sleep 1; cat "$LOG99"; echo ""
# Auth
line; info "Starting Auth..."
cd "$SFROOT/main/auth/"
LOGAUTH="$LOGDIR/auth.log"; : > "$LOGAUTH"
./vrunner --daemon --pid-path=pid --file=auth >> "$LOGAUTH" 2>&1 &
sleep 1; cat "$LOGAUTH"; echo ""
line; ok "Server started! Logs: $LOGDIR"
}
stop_server() {
title "Stop Anka2 Server"
_log "INFO" "Server stop"
discord "**Anka2** Server stopped - $(date '+%d.%m.%Y %H:%M')"
pkill -f "file=auth" 2>/dev/null && ok "Auth stopped." || warn "Auth was not running."
sleep 1
pkill -f "file=game" 2>/dev/null && ok "Channels stopped." || warn "Channels were not running."
pkill -f "file=game99" 2>/dev/null; sleep 1
pkill -f "file=db" 2>/dev/null && ok "DB stopped." || warn "DB was not running."
sleep 1
pkill -x game 2>/dev/null; pkill -x db 2>/dev/null
pkill -x auth 2>/dev/null; pkill -x vrunner 2>/dev/null
line; ok "Server fully stopped."
}
server_status() {
title "Anka2 Process Status"
# DB
show_proc "db" "$(pgrep -x db 2>/dev/null)"
# Auth
_ap=""
for _p in $(ps ax 2>/dev/null | grep -E " auth" | grep -v grep | awk '{print $1}'); do
_c=$(procstat -f "$_p" 2>/dev/null | grep " cwd " | awk '{print $NF}')
[ "$_c" = "$SFROOT/main/auth" ] && _ap="$_p" && break
done
show_proc "auth" "$_ap"
# Channel99
show_proc "channel99" "$(ps ax 2>/dev/null | grep 'game99' | grep -v grep | awk '{print $1}' | head -1)"
# Channels
for i1 in 1 2 3 4; do
for i2 in 1 2 3; do
show_proc "ch${i1}/core${i2}" "$(pid_by_dir "$SFROOT/main/channels/channel${i1}/core${i2}")"
done
done
line
# Services
_dbm=$(sockstat -l 2>/dev/null | grep -c "mysql.sock")
[ "$_dbm" -gt 0 ] && printf " ${G}●${RES} MariaDB active\n" || printf " ${R}○${RES} MariaDB stopped\n"
_wd=$([ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null && echo "${G}active${RES}" || echo "${R}stopped${RES}")
printf " ${C}›${RES} Watchdog: %b\n" "$_wd"
# Stats
CON=$(netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep -c ESTABLISHED)
RAM=$(ps ax 2>/dev/null | grep -E " (game|auth|db)( |$)" | awk '{s+=$5}END{printf "%.0f",s/1024}')
printf " ${C}›${RES} Players online : ${W}%s${RES}\n" "$CON"
printf " ${C}›${RES} Total MT2 RAM : ${W}%sMB${RES}\n" "$RAM"
line
}
# ══════════════════════════════════════════════════
# HEALTH CHECK
# ══════════════════════════════════════════════════
health_check() {
title "Pre-Start Health Check"
E=0
# Disk
PCT=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
[ "$PCT" -gt 90 ] 2>/dev/null && { err "Disk full: ${PCT}%!"; E=$((E+1)); } || ok "Disk: ${PCT}% used"
# MariaDB
check_mysql
sockstat -l 2>/dev/null | grep -q "mysql.sock" && ok "MariaDB responding" || { err "MariaDB not responding!"; E=$((E+1)); }
# Socket
[ -S /tmp/mysql.sock ] && ok "MySQL socket OK" || { err "MySQL socket missing!"; E=$((E+1)); }
# Binaries
for BIN in game db auth; do
[ -f "$BIN_DIR/$BIN" ] && [ -x "$BIN_DIR/$BIN" ] \
&& ok "Binary $BIN OK" || { err "Binary $BIN MISSING/not executable!"; E=$((E+1)); }
done
# Free ports
for PORT in 30001 30101 30201; do
sockstat -l 2>/dev/null | grep -q ":$PORT " \
&& { warn "Port $PORT in use! Server already running?"; E=$((E+1)); } \
|| ok "Port $PORT free"
done
# RAM
FREE=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
[ "$FREE" -lt 512 ] 2>/dev/null && { err "Insufficient free RAM: ${FREE}MB"; E=$((E+1)); } || ok "Free RAM: ${FREE}MB"
# LD_32
[ -n "$LD_32_LIBRARY_PATH" ] && ok "LD_32_LIBRARY_PATH set" || { err "LD_32_LIBRARY_PATH missing!"; E=$((E+1)); }
line
[ "$E" -eq 0 ] && { ok "Health check PASSED!"; discord "Anka2: Health check OK"; } \
|| { err "Health check FAILED - $E issue(s)!"; discord "Anka2: Health check failed - $E issue(s)!"; }
_log "INFO" "Health check: $E errors"
line
}
# ══════════════════════════════════════════════════
# ROLLING RESTART
# ══════════════════════════════════════════════════
rolling_restart() {
title "Rolling Restart"
warn "Channels will be restarted one by one without full downtime."
printf " How many active channels (1-4): "; read RCH; RCH="${RCH:-1}"
printf " Confirm? (y/n): "; read r; [ "$r" != "y" ] && [ "$r" != "Y" ] && return
_log "INFO" "Rolling restart $RCH channels"
discord "**Anka2** Rolling restart initiated..."
i1=1
while [ $i1 -le $RCH ]; do
line; info "Rolling restart Channel $i1..."
for i2 in 1 2 3; do
_p=$(pid_by_dir "$SFROOT/main/channels/channel${i1}/core${i2}")
[ -n "$_p" ] && { kill -SIGTERM "$_p" 2>/dev/null; sleep 2; kill -0 "$_p" 2>/dev/null && kill -9 "$_p" 2>/dev/null; ok "ch${i1}/core${i2} stopped."; }
done
sleep 2
for i2 in 1 2 3; do
info "Restarting ch${i1}/core${i2}..."
cd "$SFROOT/main/channels/channel${i1}/core${i2}/" 2>/dev/null || continue
LOGCH="$LOGDIR/ch${i1}_c${i2}.log"; : > "$LOGCH"
./vrunner --daemon --pid-path=pid --file=game >> "$LOGCH" 2>&1 &
sleep 2; cat "$LOGCH"; ok "ch${i1}/core${i2} restarted."
done
ok "Channel $i1 rolling restart OK."
discord "Anka2: Channel $i1 OK"
[ $i1 -lt $RCH ] && sleep 5
i1=$(( i1 + 1 ))
done
line; ok "Rolling restart complete!"; discord "Anka2: Rolling restart complete!"
}
# ══════════════════════════════════════════════════
# LIVE DASHBOARD
# ══════════════════════════════════════════════════
live_dashboard() {
info "Press CTRL+C to exit..."; sleep 1
while true; do
clear
printf "${R} ╔══════════════════════════════════════════════════╗\n"
printf " ║ Anka2 › Live Dashboard › %s ║\n" "$(date '+%H:%M:%S')"
printf " ╚══════════════════════════════════════════════════╝${RES}\n\n"
MT=$(sysctl -n hw.physmem | awk '{printf "%.0f",$1/1024/1024}')
MU=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
CON=$(netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep -c ESTABLISHED)
LOAD=$(uptime | awk -F'load averages:' '{print $2}' | xargs)
printf " ${DG}RAM:${RES} ${Y}%sMB${RES}/${G}%sMB${RES} ${DG}Load:${RES} ${C}%s${RES} ${DG}Online:${RES} ${G}%s${RES}\n\n" "$MU" "$MT" "$LOAD" "$CON"
# RAM Bar
BAR=$(( MU * 40 / MT )); [ $BAR -gt 40 ] && BAR=40
printf " RAM ["; i=0
while [ $i -lt 40 ]; do [ $i -lt $BAR ] && printf "${G}█${RES}" || printf "${DG}░${RES}"; i=$((i+1)); done
printf "] %s%%\n\n" "$(( MU * 100 / MT ))"
line
printf "${B}${W} ACTIVE PROCESSES${RES}\n"
_tot=0
for _p in $(pgrep -x game 2>/dev/null) $(pgrep -x db 2>/dev/null); do
_cw=$(procstat -f "$_p" 2>/dev/null | grep " cwd " | awk '{print $NF}' | sed "s|$SFROOT/main/||")
_r=$(ps -o rss= -p "$_p" 2>/dev/null | awk '{printf "%.0f",$1/1024}')
_c=$(ps -o pcpu= -p "$_p" 2>/dev/null | xargs)
_b=$(( _r * 15 / 400 )); [ $_b -gt 15 ] && _b=15
printf " ${G}●${RES} %-22s ${Y}%-6sMB${RES} ${C}%-6s%%${RES} [" "${_cw}" "$_r" "$_c"
i=0; while [ $i -lt 15 ]; do [ $i -lt $_b ] && printf "${G}█${RES}" || printf "${DG}░${RES}"; i=$((i+1)); done
printf "]\n"
_tot=$(( _tot + _r ))
done
line
printf " ${DG}Total MT2 RAM:${RES} ${Y}%sMB${RES} ${DG}Refresh 3s › CTRL+C stop${RES}\n" "$_tot"
sleep 3
done
}
# ══════════════════════════════════════════════════
# RAM MONITOR / MEMORY LEAK
# ══════════════════════════════════════════════════
ram_monitor() {
title "RAM Monitor & Memory Leak Detection"
printf " ${G}1${RES}) Start ${G}2${RES}) Stop ${G}3${RES}) Status & alerts ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
[ -f "$MONITOR_PID" ] && kill -0 $(cat "$MONITOR_PID") 2>/dev/null && warn "Monitor already active." && return
printf " RAM threshold MB (default 800): "; read PRAG; PRAG="${PRAG:-800}"
printf " Interval sec (default 120): "; read INT; INT="${INT:-120}"
WH=$(cat "$DISCORD_CFG" 2>/dev/null)
cat > /tmp/anka2_mon.sh << SCR
#!/bin/sh
export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat
while true; do
for P in \$(pgrep -x game 2>/dev/null); do
R=\$(ps -o rss= -p "\$P" 2>/dev/null | awk '{printf "%.0f",\$1/1024}')
C=\$(procstat -f "\$P" 2>/dev/null | grep " cwd " | awk '{print \$NF}')
if [ "\$R" -gt "$PRAG" ] 2>/dev/null; then
echo "[\$(date '+%d.%m.%Y %H:%M:%S')] [ALERT RAM] \$C - \${R}MB > ${PRAG}MB" >> "$ADMIN_LOG"
[ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"**Anka2 RAM Alert:** \$C = \${R}MB (threshold: ${PRAG}MB)\"}" "$WH" 2>/dev/null
fi
done
sleep $INT
done
SCR
chmod +x /tmp/anka2_mon.sh
sh /tmp/anka2_mon.sh & echo $! > "$MONITOR_PID"
ok "RAM monitor started (threshold:${PRAG}MB interval:${INT}s)"
;;
2)
[ -f "$MONITOR_PID" ] && kill $(cat "$MONITOR_PID") 2>/dev/null && rm -f "$MONITOR_PID" && ok "Monitor stopped." || warn "Not running."
;;
3)
[ -f "$MONITOR_PID" ] && kill -0 $(cat "$MONITOR_PID") 2>/dev/null \
&& ok "Monitor active PID:$(cat $MONITOR_PID)" || warn "Monitor stopped."
info "Last RAM alerts:"
grep "ALERT RAM" "$ADMIN_LOG" 2>/dev/null | tail -10 | while read L; do printf " ${Y}%s${RES}\n" "$L"; done
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# PLAYER STATISTICS
# ══════════════════════════════════════════════════
statistics() {
title "Player Statistics"
printf " ${B}${C}MariaDB root password: ${RES}"; read RP
M=$(mysql_bin)
printf " ${DG}Total accounts :${RES} ${W}%s${RES}\n" "$($M -u root -p"$RP" -se 'SELECT COUNT(*) FROM account.account;' 2>/dev/null || echo N/A)"
printf " ${DG}Total characters:${RES} ${W}%s${RES}\n" "$($M -u root -p"$RP" -se 'SELECT COUNT(*) FROM player.player;' 2>/dev/null || echo N/A)"
printf " ${DG}Online now :${RES} ${W}%s${RES}\n" "$(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED)"
echo ""; printf " ${Y}Last 5 accounts created:${RES}\n"
$M -u root -p"$RP" -se "SELECT login,create_time FROM account.account ORDER BY create_time DESC LIMIT 5;" 2>/dev/null \
| while read L; do printf " ${DG}→ %s${RES}\n" "$L"; done
line
}
# ══════════════════════════════════════════════════
# PORT CHECK
# ══════════════════════════════════════════════════
check_ports() {
title "Metin2 Port Check"
for PORT in 30001 30002 30003 30004 30101 30102 30103 30104 30105 30106 \
30201 30202 30203 30204 30205 30206 30301 30302 30303 30304 30305 30306 \
30401 30402 30403 30404 30405 30406; do
sockstat -l 2>/dev/null | grep -q ":$PORT " \
&& printf " ${G}● Port %-6s active${RES}\n" "$PORT" \
|| printf " ${R}○ Port %-6s closed${RES}\n" "$PORT"
done
line
sockstat -l 2>/dev/null | grep -q ":3306 " \
&& printf " ${G}● MariaDB 3306 active${RES}\n" \
|| printf " ${R}○ MariaDB 3306 closed${RES}\n"
printf " ${Y}› SSH port: %s${RES}\n" "$(grep '^Port' /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')"
line
}
# ══════════════════════════════════════════════════
# LOG VIEWER
# ══════════════════════════════════════════════════
view_logs() {
title "Log Viewer"
printf " ${G}1${RES}) Auth ${G}2${RES}) Ch99 ${G}3$#!/bin1 ${G}4${RES}) Ch2 ${G}5${RES}) Ch3 ${G}6${RES}) Ch4\n"
printf " ${Y}s${RES}) Search all ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1) F="$SFROOT/main/auth/syserr" ;;
2) F="$SFROOT/main/channels/channel99/syserr" ;;
3) F="$SFROOT/main/channels/channel1/core1/syserr" ;;
4) F="$SFROOT/main/channels/channel2/core1/syserr" ;;
5) F="$SFROOT/main/channels/channel3/core1/syserr" ;;
6) F="$SFROOT/main/channels/channel4/core1/syserr" ;;
s|S)
printf " Search term: "; read T
find "$SFROOT/main" -name "syserr" 2>/dev/null | while read f; do
C=$(grep -c "$T" "$f" 2>/dev/null)
[ "$C" -gt 0 ] && printf " ${Y}%s: %s result(s)${RES}\n" "$f" "$C" && grep "$T" "$f" | tail -3
done; return ;;
0) return ;;
esac
[ -f "$F" ] && { line; tail -30 "$F"; line; } || err "Log not found: $F"
}
# ══════════════════════════════════════════════════
# ADVANCED WATCHDOG
# ══════════════════════════════════════════════════
watchdog() {
title "Advanced Watchdog"
printf " ${G}1${RES}) Start ${G}2${RES}) Stop ${G}3${RES}) Status & crashes ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
[ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null && warn "Watchdog already active." && return
printf " Interval sec (default 30): "; read INT; INT="${INT:-30}"
printf " Max restarts in 5min (default 3): "; read MR; MR="${MR:-3}"
WH=$(cat "$DISCORD_CFG" 2>/dev/null)
cat > /tmp/anka2_wd_script.sh << WD
#!/bin/sh
export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat
CRASH=0; LAST=\$(date +%s)
while true; do
NOW=\$(date +%s); [ \$(( NOW - LAST )) -gt 300 ] && CRASH=0 && LAST=\$NOW
for P in db auth; do
if ! ps ax 2>/dev/null | grep -qE " \$P( |\$)"; then
CRASH=\$(( CRASH + 1 ))
TS=\$(date '+%d.%m.%Y %H:%M:%S')
echo "[\$TS] CRASH: \$P crashed (#\$CRASH)" >> "$CRASH_LOG"
echo "[\$TS] [WATCHDOG] \$P crashed" >> "$ADMIN_LOG"
if [ "\$CRASH" -le "$MR" ]; then
[ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"**Anka2:** \$P crashed! Auto-restart #\$CRASH\"}" "$WH" 2>/dev/null
else
[ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"**Anka2 ALARM:** \$P crashed \$CRASH times! Manual intervention required!\"}" "$WH" 2>/dev/null
fi
fi
done
GC=\$(pgrep -x game 2>/dev/null | wc -l | xargs)
[ "\$GC" -eq 0 ] && { echo "[\$(date '+%d.%m.%Y %H:%M:%S')] CRASH: All game processes crashed" >> "$CRASH_LOG"; [ -n "$WH" ] && fetch -qo /dev/null --post-data="{\"content\":\"**Anka2:** All channels crashed!\"}" "$WH" 2>/dev/null; }
sleep $INT
done
WD
chmod +x /tmp/anka2_wd_script.sh
sh /tmp/anka2_wd_script.sh & echo $! > "$WATCHDOG_PID"
ok "Watchdog started PID:$(cat $WATCHDOG_PID) interval:${INT}s maxRestart:${MR}"
;;
2)
[ -f "$WATCHDOG_PID" ] && kill $(cat "$WATCHDOG_PID") 2>/dev/null && rm -f "$WATCHDOG_PID" && ok "Watchdog stopped." || warn "Not running."
;;
3)
[ -f "$WATCHDOG_PID" ] && kill -0 $(cat "$WATCHDOG_PID") 2>/dev/null \
&& ok "Watchdog active PID:$(cat $WATCHDOG_PID)" || warn "Watchdog stopped."
info "Last crashes:"
[ -f "$CRASH_LOG" ] && tail -15 "$CRASH_LOG" | while read L; do printf " ${R}%s${RES}\n" "$L"; done || info "No crashes recorded."
printf " ${C}Total crashes: ${W}%s${RES}\n" "$(wc -l < "$CRASH_LOG" 2>/dev/null || echo 0)"
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# SCHEDULER
# ══════════════════════════════════════════════════
scheduler() {
title "Automatic Scheduler"
printf " ${G}1${RES}) Daily restart ${G}2${RES}) Daily backup ${G}3${RES}) Log cleanup\n"
printf " ${G}4${RES}) Show tasks ${G}5${RES}) Delete task ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
printf " Restart hour (0-23, default 6): "; read O; O="${O:-6}"
( crontab -l 2>/dev/null | grep -v "anka2_restart"
echo "# anka2_restart"
echo "0 $O * * * export LD_32_LIBRARY_PATH=/usr/lib32:/usr/local/lib32:/usr/local/lib32/compat; pkill -f 'file=auth'; sleep 1; pkill -f 'file=game'; pkill -f 'file=game99'; sleep 1; pkill -f 'file=db'; echo '[SCHEDULER] Restart' >> $ADMIN_LOG"
) | crontab -
ok "Daily restart at $O:00"
;;
2)
printf " Backup hour (0-23, default 3): "; read O; O="${O:-3}"
( crontab -l 2>/dev/null | grep -v "anka2_backup"
echo "# anka2_backup"
echo "0 $O * * * mkdir -p $BACKUP_DIR/\$(date +%d.%m.%Y) && tar czf $BACKUP_DIR/\$(date +%d.%m.%Y)/mysql.tar.gz /var/db/mysql 2>/dev/null && echo '[SCHEDULER] DB Backup' >> $ADMIN_LOG"
) | crontab -
ok "Daily backup at $O:00"
;;
3)
printf " Every N days (default 7): "; read Z; Z="${Z:-7}"
( crontab -l 2>/dev/null | grep -v "anka2_clean"
echo "# anka2_clean"
echo "0 4 */$Z * * find $SFROOT/main -name 'syserr' -size +10M -exec truncate -s 0 {} \; && echo '[SCHEDULER] Logs cleaned' >> $ADMIN_LOG"
) | crontab -
ok "Log cleanup every $Z days"
;;
4)
line; crontab -l 2>/dev/null | grep -E "# anka2|anka2_" | while read L; do printf " ${C}%s${RES}\n" "$L"; done; line
;;
5)
printf " Task (restart/backup/clean/hourly/all): "; read T
( crontab -l 2>/dev/null | grep -v "anka2#!/bincrontab - && ok "Task $T removed."
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# DISCORD
# ══════════════════════════════════════════════════
configure_discord() {
title "Discord Webhook"
printf " ${G}1${RES}) Set URL ${G}2${RES}) Test ${G}3${RES}) Hourly report ${G}4${RES}) Manual report ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1) printf " Webhook URL: "; read U; echo "$U" > "$DISCORD_CFG"; ok "Webhook saved." ;;
2)
WH=$(cat "$DISCORD_CFG" 2>/dev/null); [ -z "$WH" ] && err "Not configured!" && return
#\$CRASH\#\$CRASH\l --post-data='{"content":"**Anka2:** Webhook working!"}' "$WH" 2>/dev/null && ok "Test sent!" || err "Error!"
;;
3)
WH=$(cat "$DISCORD_CFG" 2>/dev/null); [ -z "$WH" ] && err "Not configured!" && return
( crontab -l 2>/dev/null | grep -v "anka2_hourly"
echo "# anka2_hourly"
echo "0 * * * * CON=\$(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED); fetch -qo /dev/null --post-data=\"{\\\"content\\\":\\\"**Anka2** Online: \$CON | \$(date '+%H:%M')\\\"}\" \"$WH\" 2>/dev/null"
) | crontab -
ok "Hourly report configured."
;;
4)
WH=$(cat "$DISCORD_CFG" 2>/dev/null); [ -z "$WH" ] && err "Not configured!" && return
CON=$(netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep -c ESTABLISHED)
MU=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
LD=$(uptime | awk -F'load averages:' '{print $2}' | xargs)
fetch -qo /dev/null --post-data="{\"content\":\"**Anka2** $(date '+%d.%m.%Y %H:%M')\n
Online: $CON\n
RAM: ${MU}MB\n
Load: $LD\"}" "$WH" 2>/dev/null \
&& ok "Report sent!" || err "Error!"
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# DB MONITOR
# ══════════════════════════════════════════════════
db_monitor() {
title "Database Monitor"
printf " ${G}1${RES}) Tables ${G}2${RES}) Duplicates ${G}3${RES}) Inactive >90d ${G}4${RES}) Console ${G}5${RES}) Password ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
[ "$opt" = "0" ] && return
printf " MariaDB root password: "; read RP
M=$(mysql_bin)
case "$opt" in
1) $M -u root -p"$RP" -e "SELECT table_schema,table_name,ROUND((data_length+index_length)/1024/1024,2) AS MB FROM information_schema.tables ORDER BY (data_length+index_length) DESC LIMIT 20;" 2>/dev/null | while read L; do printf " ${DG}%s${RES}\n" "$L"; done ;;
2) $M -u root -p"$RP" -e "SELECT name,COUNT(*) cnt FROM player.player GROUP BY name HAVING cnt>1 LIMIT 20;" 2>/dev/null | while read L; do printf " ${Y}%s${RES}\n" "$L"; done ;;
3) $M -u root -p"$RP" -e "SELECT login,last_play FROM account.account WHERE last_play < DATE_SUB(NOW(),INTERVAL 90 DAY) ORDER BY last_play LIMIT 20;" 2>/dev/null | while read L; do printf " ${DG}%s${RES}\n" "$L"; done ;;
4) $M -u root -p"$RP" 2>/dev/null ;;
5)
printf " New password: "; read NP; printf " Confirm: "; read NP2
[ "$NP" != "$NP2" ] && err "Passwords do not match!" && return
$M -u root -p"$RP" -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$NP'; ALTER USER 'root'@'%' IDENTIFIED BY '$NP'; FLUSH PRIVILEGES;" 2>/dev/null && ok "Password changed!" || err "Error!"
;;
esac; line
}
# ══════════════════════════════════════════════════
# GM MANAGEMENT
# ══════════════════════════════════════════════════
gm_management() {
title "GM Management"
printf " ${G}1${RES}) List GMs ${G}2${RES}) Add GM ${G}3${RES}) Remove GM ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
[ "$opt" = "0" ] && return
printf " MariaDB root password: "; read RP
M=$(mysql_bin)
case "$opt" in
1) $M -u root -p"$RP" -e "SELECT login,status FROM account.account WHERE status='GM' LIMIT 20;" 2>/dev/null | while read L; do printf " ${Y}%s${RES}\n" "$L"; done ;;
2) printf " Username: "; read GM; $M -u root -p"$RP" -e "UPDATE account.account SET status='GM' WHERE login='$GM';" 2>/dev/null && ok "GM added: $GM" || err "Error!"; _log "INFO" "GM added: $GM" ;;
3) printf " Username: "; read GM; $M -u root -p"$RP" -e "UPDATE account.account SET status='OK' WHERE login='$GM';" 2>/dev/null && ok "GM removed: $GM" || err "Error!"; _log "INFO" "GM removed: $GM" ;;
esac; line
}
# ══════════════════════════════════════════════════
# COMPILE SERVER
# ══════════════════════════════════════════════════
compile_server() {
title "Compile Server"
printf " ${G}1${RES}) DB ${G}2${RES}) Game ${G}3${RES}) All ${Y}4${RES}) Clean+All ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
[ ! -d "$SRC_DIR" ] && err "Source directory not found!" && return
cd "$SRC_DIR"
case "$opt" in
1) gmake db && ok "DB compiled!" || err "DB error!" ;;
2) gmake game && ok "Game compiled!" || err "Game error!" ;;
3) gmake db && gmake game && ok "Full compile done!" || err "Error!" ;;
4) gmake clean; gmake db && gmake game && ok "Clean+Compile OK!" || err "Error!" ;;
0) return ;;
esac
_log "INFO" "Compile opt:$opt"; line
}
# ══════════════════════════════════════════════════
# UPDATE BINARIES
# ══════════════════════════════════════════════════
update_binaries() {
title "Update Binaries"
warn "Server must be stopped before update!"
printf " Continue? (y/n): "; read r; [ "$r" != "y" ] && [ "$r" != "Y" ] && return
TS=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BIN_DIR/backup_$TS"
[ -f "$BIN_DIR/game" ] && cp "$BIN_DIR/game" "$BIN_DIR/backup_$TS/game.old" && ok "Game backup saved."
[ -f "$BIN_DIR/db" ] && cp "$BIN_DIR/db" "$BIN_DIR/backup_$TS/db.old" && ok "DB backup saved."
cd "$SRC_DIR" && gmake db && gmake game && ok "Binaries updated!" || err "Compile error!"
_log "INFO" "Binary update backup:$BIN_DIR/backup_$TS"; line
}
# ══════════════════════════════════════════════════
# VERSIONING
# ══════════════════════════════════════════════════
versioning() {
title "Versioning & Rollback"
printf " ${G}1${RES}) Save ${G}2${RES}) List ${G}3${RES}) Rollback ${G}4${RES}) Delete ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
mkdir -p "$VERSIONS_DIR"
case "$opt" in
1)
printf " Tag (e.g. v1.0): "; read TAG; TAG="${TAG:-$(date +%Y%m%d_%H%M%S)}"
mkdir -p "$VERSIONS_DIR/$TAG"
[ -f "$BIN_DIR/game" ] && cp "$BIN_DIR/game" "$VERSIONS_DIR/$TAG/game" && ok "game saved."
[ -f "$BIN_DIR/db" ] && cp "$BIN_DIR/db" "$VERSIONS_DIR/$TAG/db" && ok "db saved."
date '+%d.%m.%Y %H:%M:%S' > "$VERSIONS_DIR/$TAG/timestamp"
ok "Version '$TAG' saved."; _log "INFO" "Version: $TAG"
;;
2)
line; ls -lt "$VERSIONS_DIR" 2>/dev/null | grep "^d" | while read L; do
T=$(echo "$L" | awk '{print $NF}')
TS=$(cat "$VERSIONS_DIR/$T/timestamp" 2>/dev/null)
printf " ${C}%-30s${RES} %s\n" "$T" "$TS"
done; line
;;
3)
ls "$VERSIONS_DIR" 2>/dev/null; printf " Version: "; read TAG
[ ! -d "$VERSIONS_DIR/$TAG" ] && err "Not found!" && return
warn "Server will be stopped!"; printf " Confirm? (y/n): "; read r
[ "$r" != "y" ] && [ "$r" != "Y" ] && return
pkill -f "file=auth" 2>/dev/null; pkill -f "file=game" 2>/dev/null
pkill -f "file=game99" 2>/dev/null; pkill -f "file=db" 2>/dev/null; sleep 2
[ -f "$VERSIONS_DIR/$TAG/game" ] && cp "$VERSIONS_DIR/$TAG/game" "$BIN_DIR/game" && ok "game restored."
[ -f "$VERSIONS_DIR/$TAG/db" ] && cp "$VERSIONS_DIR/$TAG/db" "$BIN_DIR/db" && ok "db restored."
ok "Rollback to '$TAG' complete."; _log "INFO" "Rollback: $TAG"
;;
4)
ls "$VERSIONS_DIR" 2>/dev/null; printf " Version to delete: "; read TAG
rm -rf "$VERSIONS_DIR/$TAG" && ok "Version '$TAG' deleted."
;;
0) return ;;
esac; line
}
# ══════════════════════════════════════════════════
# DDOS PROTECTION
# ══════════════════════════════════════════════════
ddos_protection() {
title "DDoS Protection & Security"
printf " ${G}1${RES}) Rate limiting ${G}2${RES}) Blocked IPs ${G}3${RES}) Suspicious IPs ${G}4${RES}) SSH Audit ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
printf " Max connections/IP (default 10): "; read MAX; MAX="${MAX:-10}"
cat > /etc/pf.conf << PFEOF
ext_if = "vtnet0"
table <sshguard> persist
table <ddos_block> persist
set block-policy drop
block all
pass quick on lo0 all
pass out quick on \$ext_if all keep state
block in quick on \$ext_if from <sshguard>
block in quick on \$ext_if from <ddos_block>
pass in quick on \$ext_if proto tcp to port 223 keep state
pass in quick on \$ext_if proto tcp to port {30001 30002 30003 30004} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30101 30102 30103 30104 30105 30106} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30201 30202 30203 30204 30205 30206} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30301 30302 30303 30304 30305 30306} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on \$ext_if proto tcp to port {30401 30402 30403 30404 30405 30406} keep state (max-src-conn $MAX, max-src-conn-rate 20/5, overload <ddos_block> flush global)
pass in quick on lo0 proto tcp to port 3306 keep state
pass in quick on \$ext_if proto tcp to port 3306 keep state
PFEOF
pfctl -F all -f /etc/pf.conf 2>/dev/null && ok "Rate limiting applied ($MAX/IP)" || err "pf error!"
_log "INFO" "Rate limiting: $MAX/IP"
;;
2)
line; info "ddos_block table:"
pfctl -t ddos_block -T show 2>/dev/null | while read IP; do printf " ${R}✗ %s${RES}\n" "$IP"; done
printf "\n Clear table? (y/n): "; read r
[ "$r" = "y" ] || [ "$r" = "Y" ] && pfctl -t ddos_block -T flush 2>/dev/null && ok "Table cleared."
;;
3)
line; netstat -an 2>/dev/null | grep -E "300[0-9][0-9]" | grep ESTABLISHED \
| awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -15 \
| while read C IP; do
[ "$C" -gt 5 ] && printf " ${R}⚠ %-20s %s conn${RES}\n" "$IP" "$C" \
|| printf " ${G}● %-20s %s conn${RES}\n" "$IP" "$C"
done; line
;;
4)
line; info "Last 20 SSH logins:"; last -20 | head -20 | while read L; do printf " ${DG}%s${RES}\n" "$L"; done
line; info "Recent failures:"; grep "Failed\|Invalid" /var/log/auth.log 2>/dev/null | tail -10 | while read L; do printf " ${R}%s${RES}\n" "$L"; done
line
;;
0) return ;;
esac
}
# ══════════════════════════════════════════════════
# CLEAN LOGS
# ══════════════════════════════════════════════════
clean_logs() {
title "Clean Logs"
_clf() { rm -rf log/* start.log packet_info.txt syserr syslog stdout PTS p2p_packet_info.txt mob_count DEV_LOG.log version.txt udp_packet_info.txt usage.txt mob_data.txt pid game.core db.core; }
cd "$SFROOT/main/db/" && _clf
for i1 in 1 2 3 4; do for i2 in 1 2 3; do cd "$SFROOT/main/channels/channel${i1}/core${i2}/" 2>/dev/null && _clf; done; done
cd "$SFROOT/main/channels/channel99/" && _clf
cd "$SFROOT/main/auth/" && _clf
cd /var/db/mysql/ && rm -rf *.err *.pid
line; ok "Logs cleaned."; _log "INFO" "Logs cleaned"
}
# ══════════════════════════════════════════════════
# COMPILE QUESTS
# ══════════════════════════════════════════════════
compile_quests() {
title "Compile Quests"
cd "$SFROOT/share/locale/europe/quest/"
[ -r quest_list ] && rm -rdf object && mkdir object \
&& while read L; do ./qc $L; done < quest_list \
&& ok "Quests compiled." && _log "INFO" "Quests compiled" \
|| err "quest_list not found!"
line
}
# ══════════════════════════════════════════════════
# BACKUP
# ══════════════════════════════════════════════════
backup_server() {
title "Server Backup"
_log "INFO" "Backup initiated"
TGT="$BACKUP_DIR/$(date +%d.%m.%Y_%H.%M)"; mkdir -p "$TGT"
info "Backing up server → anka2_game.tar.gz"
tar czf "$TGT/anka2_game.tar.gz" /usr/home 2>/dev/null && ok "Server backup OK."
info "Backing up DB → anka2_mysql.tar.gz"
tar czf "$TGT/anka2_mysql.tar.gz" /var/db/mysql 2>/dev/null && ok "DB backup OK."
line; ok "Saved to: $TGT"; du -sh "$TGT"
_log "INFO" "Backup: $TGT"; line
}
# ══════════════════════════════════════════════════
# SYSTEM REPORT
# ══════════════════════════════════════════════════
system_report() {
F="/tmp/anka2_report_$(date +%Y%m%d_%H%M%S).txt"
MT=$(sysctl -n hw.physmem | awk '{printf "%.0f",$1/1024/1024}')
MU=$(vmstat -H 2>/dev/null | tail -1 | awk '{printf "%.0f",$4/1024}')
{
echo "═══════════════════════════════════════════════"
echo " ANKA2 - System Report - $(date '+%d.%m.%Y %H:%M:%S')"
echo " Created by Best Studio | Optimized by CodexRO"
echo "═══════════════════════════════════════════════"
echo "Hostname : $(hostname)"
echo "IP : $(ifconfig vtnet0 2>/dev/null | grep 'inet ' | awk '{print $2}')"
echo "FreeBSD : $(freebsd-version 2>/dev/null)"
echo "CPU : $(sysctl -n hw.model) ($(sysctl -n hw.ncpu) cores)"
echo "Uptime : $(uptime)"
echo "RAM : ${MU}MB / ${MT}MB"
echo "Disk : $(df -h / | tail -1 | awk '{print $3"/"$2" ("$5")"}')"
echo "Load : $(uptime | awk -F'load averages:' '{print $2}')"
echo ""
echo "--- Processes ---"
ps ax | grep -E " (game|auth|db)( |$)|game99" | grep -v grep
echo ""
echo "--- Online: $(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED) players ---"
echo ""
echo "--- Last 20 admin actions ---"
tail -20 "$ADMIN_LOG" 2>/dev/null
echo "═══════════════════════════════════════════════"
} > "$F"
ok "Report: $F"
printf " Display? (y/n): "; read r; [ "$r" = "y" ] || [ "$r" = "Y" ] && less "$F" 2>/dev/null || cat "$F"
WH=$(cat "$DISCORD_CFG" 2>/dev/null)
if [ -n "$WH" ]; then
printf " Send to Discord? (y/n): "; read r
[ "$r" = "y" ] || [ "$r" = "Y" ] && discord "**Anka2 Report** $(date '+%d.%m.%Y %H:%M') RAM:${MU}MB/${MT}MB Online:$(netstat -an 2>/dev/null | grep -E '300[0-9][0-9]' | grep -c ESTABLISHED)" && ok "Sent!"
fi
line
}
# ══════════════════════════════════════════════════
# CHANGELOG
# ══════════════════════════════════════════════════
changelog() {
title "Changelog & Notes"
printf " ${G}1${RES}) Add ${G}2${RES}) View ${G}3${RES}) Search ${G}4${RES}) Export ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
printf " Category (fix/update/config/other): "; read CAT; CAT="${CAT:-update}"
printf " Description: "; read DESC; [ -z "$DESC" ] && err "Description cannot be empty!" && return
printf "[%s] [%s] %s\n" "$(date '+%d.%m.%Y %H:%M:%S')" "$CAT" "$DESC" >> "$CHANGELOG_FILE"
ok "Entry added."
;;
2)
line
[ -f "$CHANGELOG_FILE" ] && tail -20 "$CHANGELOG_FILE" | while read L; do
echo "$L" | grep -q "\[fix\]" && printf " ${G}%s${RES}\n" "$L" && continue
echo "$L" | grep -q "\[update\]" && printf " ${C}%s${RES}\n" "$L" && continue
echo "$L" | grep -q "\[config\]" && printf " ${Y}%s${RES}\n" "$L" && continue
printf " ${DG}%s${RES}\n" "$L"
done || info "Changelog empty."
printf " ${DG}Total: %s entries${RES}\n" "$(wc -l < "$CHANGELOG_FILE" 2>/dev/null || echo 0)"
line
;;
3)
printf " Search term: "; read T
grep -i "$T" "$CHANGELOG_FILE" 2>/dev/null | while read L; do printf " ${Y}%s${RES}\n" "$L"; done
;;
4)
E="/tmp/changelog_$(date +%Y%m%d).txt"
cp "$CHANGELOG_FILE" "$E" 2>/dev/null && ok "Exported: $E" || err "Changelog empty."
;;
0) return ;;
esac; line
}
# ══════════════════════════════════════════════════
# AUTO-TUNE SYSCTL
# ══════════════════════════════════════════════════
autotune_sysctl() {
title "Auto-tune Sysctl"
MT=$(sysctl -n hw.physmem | awk '{printf "%.0f",$1/1024/1024}')
NC=$(sysctl -n hw.ncpu)
info "Detected RAM: ${MT}MB | CPU cores: $NC"
SB=$(( MT * 1024 * 64 )); [ $SB -gt 67108864 ] && SB=67108864
MF=$(( MT * 32 )); [ $MF -gt 524288 ] && MF=524288
echo ""
printf " ${C}Optimal values for ${MT}MB RAM:${RES}\n"
printf " ${DG}kern.ipc.maxsockbuf${RES} = ${Y}%s${RES}\n" "$SB"
printf " ${DG}kern.maxfiles${RES} = ${Y}%s${RES}\n" "$MF"
printf " ${DG}tcp.sendspace${RES} = ${Y}131072${RES}\n"
echo ""
printf " Apply? (y/n): "; read r; [ "$r" != "y" ] && [ "$r" != "Y" ] && return
cat > /etc/sysctl.conf << SYSCTL
# Anka2 - Auto-tune ${MT}MB RAM / ${NC} CPU - $(date '+%d.%m.%Y %H:%M:%S')
# Created by Best Studio | Optimized by CodexRO
vfs.read_max=128
kern.ipc.maxsockbuf=$SB
net.inet.ip.forwarding=1
net.inet.tcp.hostcache.expire=3900
kern.ipc.somaxconn=65535
security.bsd.see_other_uids=0
net.inet.tcp.mssdflt=1440
net.inet.tcp.nolocaltimewait=1
net.inet.tcp.sendspace=131072
net.inet.tcp.recvspace=131072
net.inet.tcp.syncookies=1
net.inet.tcp.syncache.rexmtlimit=1
net.inet.ip.portrange.randomized=1
net.inet.ip.process_options=0
kern.maxfiles=$MF
kern.maxfilesperproc=$(( MF / 2 ))
net.inet.ip.random_id=1
net.inet.ip.redirect=0
net.inet.ip.sourceroute=0
net.inet.ip.accept_sourceroute=0
net.local.stream.recvspace=32768
net.local.stream.sendspace=65535
net.inet.udp.maxdgram=57344
net.inet.icmp.bmcastecho=0
net.inet.icmp.log_redirect=1
net.inet.icmp.drop_redirect=1
net.inet.tcp.always_keepalive=0
net.inet.ip.intr_queue_maxlen=1000
net.inet.tcp.drop_synfin=1
net.inet.tcp.delayed_ack=0
net.inet.tcp.fast_finwait2_recycle=1
net.inet.tcp.icmp_may_rst=0
net.inet.tcp.msl=5000
net.inet.tcp.path_mtu_discovery=0
net.inet.tcp.rfc1323=1
net.inet.tcp.rfc3042=1
net.inet.tcp.rfc3390=1
net.inet.tcp.sack.enable=1
net.inet.udp.blackhole=1
net.inet.tcp.blackhole=2
net.inet.icmp.icmplim=2500
vm.swap_idle_enabled=1
vm.swap_idle_threshold1=15
vm.swap_idle_threshold2=10
SYSCTL
sysctl -f /etc/sysctl.conf 2>/dev/null | grep -v "^sysctl: unknown" | tail -5
ok "Sysctl optimized for ${MT}MB RAM!"
_log "INFO" "Auto-tune sysctl RAM=${MT}MB CPU=${NC}"
changelog_auto "config" "Auto-tune sysctl RAM=${MT}MB"
line
}
# ══════════════════════════════════════════════════
# BINARY INTEGRITY
# ══════════════════════════════════════════════════
binary_integrity() {
title "Binary Integrity"
printf " ${G}1${RES}) Save reference ${G}2${RES}) Verify ${G}3${RES}) Display ${R}0${RES}) Back\n"
line; printf " ${B}${C}Choice › ${RES}"; read opt
case "$opt" in
1)
: > "$CHECKSUM_FILE"
for BIN in game db auth; do
[ -f "$BIN_DIR/$BIN" ] || { warn "$BIN not found."; continue; }
MD5=$(md5 -q "$BIN_DIR/$BIN" 2>/dev/null)
SZ=$(ls -la "$BIN_DIR/$BIN" | awk '{print $5}')
printf "%s %s %s %s\n" "$MD5" "$SZ" "$BIN_DIR/$BIN" "$(date '+%d.%m.%Y %H:%M:%S')" >> "$CHECKSUM_FILE"
ok "$BIN: $MD5"
done
ok "Checksums saved: $CHECKSUM_FILE"; _log "INFO" "Checksums saved"; changelog_auto "config" "Binary checksums saved"
;;
2)
[ ! -f "$CHECKSUM_FILE" ] && err "No reference found! Run option 1 first." && return
MOD=0
while IFS= read -r L; do
SM=$(echo "$L" | awk '{print $1}')
BP=$(echo "$L" | awk '{print $3}')
BN=$(basename "$BP")
[ ! -f "$BP" ] && { err "$BN: MISSING!"; MOD=$((MOD+1)); continue; }
CM=$(md5 -q "$BP" 2>/dev/null)
[ "$CM" = "$SM" ] && ok "$BN: OK" || {
err "$BN: MODIFIED!"
printf " ${DG}Saved : %s${RES}\n" "$SM"
printf " ${R}Current : %s${RES}\n" "$CM"
MOD=$((MOD+1))
discord "Anka2: Binary $BN MODIFIED!"
_log "ERROR" "Binary $BN modified!"
}
done < "$CHECKSUM_FILE"
line
[ "$MOD" -eq 0 ] && ok "All binaries intact!" || err "$MOD binary/binaries modified!"
;;
3)
[ -f "$CHECKSUM_FILE" ] && cat "$CHECKSUM_FILE" | while read L; do printf " ${DG}%s${RES}\n" "$L"; done || info "No reference saved."
;;
0) return ;;
esac; line
}
# ══════════════════════════════════════════════════
# MAIN LOOP
# ══════════════════════════════════════════════════
_log "INFO" "Anka2 Server Manager v5.0 started - Created by Best Studio | Optimized by CodexRO"
while true; do
show_menu; read chs; echo ""
case "$chs" in
1) start_server ;;
2) stop_server ;;
3) stop_server; sleep 2; start_server ;;
4) server_status ;;
5) live_dashboard ;;
6) ram_monitor ;;
7) statistics ;;
8) check_ports ;;
9) view_logs ;;
10) title "Admin Log"; tail -40 "$ADMIN_LOG" 2>/dev/null || warn "Log empty."; line ;;
11) watchdog ;;
12) scheduler ;;
13) configure_discord ;;
14) db_monitor ;;
15) gm_management ;;
16) compile_server ;;
17) update_binaries ;;
18) versioning ;;
19) ddos_protection ;;
20) clean_logs ;;
21) compile_quests ;;
22) backup_server ;;
23) check_mysql && service mysql-server start && ok "MariaDB started." ;;
24) service mysql-server stop && ok "MariaDB stopped." ;;
25) system_report ;;
26) health_check ;;
27) rolling_restart ;;
28) changelog ;;
29) autotune_sysctl ;;
30) binary_integrity ;;
0) _log "INFO" "Manager closed"
printf "\n${G} Goodbye! Anka2 - Best Studio | CodexRO${RES}\n\n"; exit 0 ;;
*) err "Invalid choice." ;;
esac
printf "\n${DG} ─────────────────────────────────────────────────${RES}\n"
printf "${DG} Press ENTER to continue...${RES}"; read _
done
[/CODE]
replace : 223 your port ssh login
pass in quick on \$ext_if proto tcp to port 223 keep state
- Katılım
- 30 May 2025
- Konular
- 10
- Mesajlar
- 186
- Online süresi
- 6d 21h
- Reaksiyon Skoru
- 63
- Altın Konu
- 0
- TM Yaşı
- 1 Yıl 14 Gün
- Başarım Puanı
- 51
- MmoLira
- 2,051
- DevLira
- 12
In my opinion, you should try to fix the existing errors in the current systems and make the version more stable, but don't add anything new, just add the fixes to the current version.Az kaldı yarın dönüyorum GitHub sürekli güncel tutacağım el birliği ile güzel bir yapı çıkartalım malum benim real işlerim ve başka nedenlerden dolayı sizin gibi herşeyi test etmeye zamanım olmuyor. Bu zamana kadar GitHub üzerinden ilerleseydik daha sorunsuz olabilirdi neyse sağlık olsun ☺
- Katılım
- 13 Eyl 2016
- Konular
- 3
- Mesajlar
- 11
- Online süresi
- 1d 2h
- Reaksiyon Skoru
- 7
- Altın Konu
- 0
- TM Yaşı
- 9 Yıl 9 Ay 6 Gün
- Başarım Puanı
- 58
- MmoLira
- 555
- DevLira
- 3
SERVERWhat's the updated things now?
- 1 - Start — Asks how many channels (1-4), runs a pre-check, then starts DB, all channels (each with 3 cores), Channel99, and Auth
- 2 - Stop — Kills all running processes: auth, game channels, channel99, db, and vrunner
- 3 - Restart — Runs Stop followed by Start automatically
- 4 - Status — Shows all running processes with their PID, RAM usage and CPU usage
- 26 - Health Check — Checks disk space, MariaDB, MySQL socket, binaries, ports, free RAM and library paths before starting
- 27 - Rolling Restart — Restarts channels one by one without taking the whole server offline
MONITORING
- 5 - Dashboard — Live auto-refreshing view (every 3s) showing RAM bar, load, online players and per-process stats
- 6 - RAM Monitor — Background process that alerts via Discord if any game process exceeds a RAM threshold you set
- 7 - Statistics — Queries the database for total accounts, total characters, current online count and last 5 registered accounts
- 8 - Ports — Checks all Metin2 game ports, MariaDB port 3306 and your SSH port to see which are open or closed
- 9 - Logs — View the last 30 lines of syserr logs per component, or search a keyword across all logs at once
- 10 - Admin Log — Shows the last 40 entries from the admin action log
AUTOMATION
- 11 - Watchdog — Background process that monitors if db/auth/game crash and sends Discord alerts, with a configurable restart limit
- 12 - Scheduler — Sets up cron jobs for daily restart, daily DB backup, and periodic log cleanup
- 13 - Discord — Configure your webhook URL, test it, set up hourly online reports, or send a manual status report
DATABASE
- 14 - DB Monitor — View table sizes, find duplicate character names, list inactive accounts (90+ days), open a MySQL console, or change the root password
- 15 - GM Management — List all GM accounts, promote a player to GM, or demote a GM back to a regular account
COMPILE & VERSIONS
- 16 - Compile — Compile the DB binary, Game binary, both, or do a clean build from source
- 17 - Update Binaries — Backs up current binaries then recompiles and replaces them
- 18 - Versioning — Save a tagged snapshot of your binaries, list all saved versions, roll back to a previous version, or delete old versions
SECURITY
- 19 - DDoS & Audit — Apply PF firewall rate limiting rules, view/clear blocked IPs, identify suspicious IPs by connection count, or audit SSH login history
- 30 - Binary Integrity — Save MD5 checksums of your binaries as a reference, then verify them later to detect any unauthorized modifications
UTILITIES
- 20 - Clean Logs — Deletes syserr, syslog, core dumps, packet logs and other temporary files from all server directories
- 21 - Quests — Compiles all quest scripts listed in quest_list using the qc compiler
- 22 - Backup — Creates a full compressed backup of the server files and the MySQL database to the backup directory
- 23 - Start DB — Starts the MariaDB service and creates the MySQL socket symlink if missing
- 24 - Stop DB — Stops the MariaDB service
- 25 - Report — Generates a full system report (hardware, processes, online count, last admin actions) and optionally sends a summary to Discord
- 28 - Changelog — Add, view, search or export timestamped notes about changes made to the server
- 29 - Sysctl — Auto-calculates and applies optimized FreeBSD kernel network and memory settings based on your server's RAM and CPU count
Şu an konuyu görüntüleyenler (Toplam : 17, Üye: 2, Misafir: 15)
Benzer konular
- Cevaplar
- 39
- Görüntüleme
- 3K
- Cevaplar
- 39
- Görüntüleme
- 5K
- Cevaplar
- 112
- Görüntüleme
- 14K
- Cevaplar
- 121
- Görüntüleme
- 11K
- Cevaplar
- 42
- Görüntüleme
- 5K



