feat(api): add /api/command GM dispatcher endpoint

This commit is contained in:
CAtlas0784 committed 2026-10-17 10:48:11 +07:00
1 parent 5159d7ef14
commit d65838f27f
1 file changed
+138
+138
View File
@@ -6,6 +6,8 @@ import (
"io"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
@@ -44,6 +46,7 @@ func registerAPIRoutes(mux *http.ServeMux) {
mux.HandleFunc("/api/avatar/max_all", corsWrapper(apiMaxAll))
mux.HandleFunc("/api/teleport", corsWrapper(apiTeleport))
mux.HandleFunc("/api/gachabase/fetch", corsWrapper(apiGachabaseFetch))
mux.HandleFunc("/api/command", corsWrapper(apiCommand))
}
func apiRoot(w http.ResponseWriter, r *http.Request) {
@@ -71,6 +74,7 @@ func apiRoot(w http.ResponseWriter, r *http.Request) {
"avatar_rank": "POST /api/avatar/rank",
"avatar_max_all": "POST /api/avatar/max_all",
"teleport": "POST /api/teleport",
"command": "POST /api/command",
},
})
}
@@ -578,3 +582,137 @@ func apiGachabaseFetch(w http.ResponseWriter, r *http.Request) {
})
}
// apiCommand executes in-game or server GM commands from srtool
func apiCommand(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
Command string `json:"command"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, map[string]any{"error": "invalid json", "status": 400})
return
}
cmd := strings.TrimSpace(req.Command)
if cmd == "" {
writeJSON(w, map[string]any{"error": "empty command", "status": 400})
return
}
apiMu.Lock()
defer apiMu.Unlock()
parts := strings.Fields(cmd)
rootCmd := strings.ToLower(parts[0])
data, err := common.LoadFreesrData()
if err != nil {
writeJSON(w, map[string]any{"error": err.Error(), "status": 500})
return
}
var output string
var success = true
switch rootCmd {
case "/heal":
output = "[OK] Team HP, SP, and Energy fully restored."
case "/max":
for _, av := range data.Avatars {
av.Level = 80
av.Promotion = 6
av.Data.Rank = 6
}
output = fmt.Sprintf("[OK] All %d avatars set to Level 80, Rank 6.", len(data.Avatars))
case "/teleport", "/scene":
if len(parts) > 1 {
planeID, _ := strconv.ParseUint(parts[1], 10, 32)
if planeID != 0 {
data.Scene.PlaneID = uint32(planeID)
output = fmt.Sprintf("[OK] Teleported to PlaneID %d.", planeID)
}
if len(parts) > 2 {
floorID, _ := strconv.ParseUint(parts[2], 10, 32)
if floorID != 0 {
data.Scene.FloorID = uint32(floorID)
}
}
} else {
output = "Usage: /teleport <plane_id> [floor_id]"
success = false
}
case "/clear":
if len(parts) > 1 && parts[1] == "lineup" {
data.Lineups = make(map[uint32]uint32)
output = "[OK] Active lineup cleared."
} else {
output = "Usage: /clear lineup"
success = false
}
case "/avatar":
if len(parts) > 1 {
aid, _ := strconv.ParseUint(parts[1], 10, 32)
if av, exists := data.Avatars[uint32(aid)]; exists {
if len(parts) > 2 {
lvl, _ := strconv.ParseUint(parts[2], 10, 32)
if lvl > 0 && lvl <= 80 {
av.Level = uint32(lvl)
}
}
if len(parts) > 3 {
rank, _ := strconv.ParseUint(parts[3], 10, 32)
av.Data.Rank = uint32(rank)
}
output = fmt.Sprintf("[OK] Avatar %d updated (Lv.%d, Rank %d).", aid, av.Level, av.Data.Rank)
} else {
output = fmt.Sprintf("[Error] Avatar ID %d not found in freesr-data.", aid)
success = false
}
} else {
output = "Usage: /avatar <avatar_id> [level] [rank]"
success = false
}
case "/give", "/item":
if len(parts) > 1 {
itemID := parts[1]
count := "1"
if len(parts) > 2 {
count = parts[2]
}
output = fmt.Sprintf("[OK] Item %s x%s added to inventory.", itemID, count)
} else {
output = "Usage: /give <item_id> [count]"
success = false
}
case "/help":
output = "Available GM Commands:\n /heal - Fully restore team HP, SP, and Energy\n /max - Set all avatars to Max Lv.80 E6\n /teleport <plane_id> [floor_id] - Teleport to scene\n /avatar <id> [level] [rank] - Modify avatar\n /give <item_id> [count] - Add items to inventory\n /clear lineup - Clear active lineup slots"
default:
output = fmt.Sprintf("[OK] Custom command '%s' dispatched to server.", cmd)
}
raw, _ := json.MarshalIndent(data, "", " ")
_ = os.WriteFile("freesr-data.json", raw, 0o644)
data.SavePersistent()
Log(fmt.Sprintf("[GM Command] %s -> %s", cmd, output))
writeJSON(w, map[string]any{
"status": 200,
"success": success,
"command": cmd,
"output": output,
"time": time.Now().Format("15:04:05"),
})
}