noisiv 1
noisiv
Manwe Work 1
Manwe Work
Karan2offical 1
Karan2offical
Fethi Polat 1
Fethi Polat
ShadowFon 1
ShadowFon
bikral 1
bikral
-TuRKuaZ- 1
-TuRKuaZ-
SLyFeLLowTR 1
SLyFeLLowTR
TGamesZeus 1
TGamesZeus
Best Studio 1
Best Studio
berkmenoo 1
berkmenoo
InfernoShade 1
InfernoShade
Hikaye Ekle

Altın Konu Anka2 Project SF | 2025

4.97 yıldız(lar) 35 Değerlendirme Değerlendirenler

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!

cuando abro el cliente me sale en modo desarrolador y no me permite jugarlo
 
Yalnızca Python kodunu devre dışı bırakarak kullanıcı arayüzü hata ayıklamasını kapatabilirsiniz.@

clafo1234

 
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 :D
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
Bu .sh dosya yönetim özelliği güçlü ve harika.
 
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 :D
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
Great ı liked it very much :)
 
dosya paylaşabilirmisin reisim bizde aktif edelim
sorunsuz hale geldiğinde paylaşılabilir bir kaç sorunu mevcut. Official karakter seçme ve oluşturma sistemi tekrar orjinal haline getirildi. Filese çok emek harcandığı için @Best Studio ne kadar teşekkür edilse azdır.
 

Ekli dosyalar

  • Ekran görüntüsü 2026-03-26 192642.png
    Ekran görüntüsü 2026-03-26 192642.png
    3.5 MB · Görüntüleme: 0
  • 0326_192520.png
    0326_192520.png
    1.7 MB · Görüntüleme: 0
  • Ekran görüntüsü 2026-03-26 193144.png
    Ekran görüntüsü 2026-03-26 193144.png
    2.3 MB · Görüntüleme: 0
  • Ekran görüntüsü 2026-03-26 193159.png
    Ekran görüntüsü 2026-03-26 193159.png
    2.3 MB · Görüntüleme: 0
Son düzenleme:

Şu an konuyu görüntüleyenler (Toplam : 2, Üye: 1, Misafir: 1)

Geri
Üst