DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
This commit is contained in:
commit
645b64fe21
4388 files changed
+1405457
No files matched your search
@@ -0,0 +1,194 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"longheng.io/server/internal/platform/httpx"
|
||||
"longheng.io/server/internal/reference/player"
|
||||
"longheng.io/server/pkg/contracts"
|
||||
)
|
||||
|
||||
func (s *Service) registerRoutes(mux *http.ServeMux) {
|
||||
prefix := s.adminPrefix
|
||||
mux.HandleFunc(prefix+"/players", s.admin(s.handlePlayers))
|
||||
mux.HandleFunc(prefix+"/player_summaries", s.admin(s.handleSummaries))
|
||||
mux.HandleFunc(prefix+"/player_summaries/rebuild", s.admin(s.handleSummaryRebuild))
|
||||
mux.HandleFunc(prefix+"/readmodels/player_summaries", s.admin(s.handleViewSearch("player_summaries")))
|
||||
mux.HandleFunc(prefix+"/readmodels/player_summaries/rebuild", s.admin(s.handleViewRebuild("player_summaries")))
|
||||
mux.HandleFunc(prefix+"/accounts", s.admin(s.handleAccounts))
|
||||
mux.HandleFunc(prefix+"/playerstore", s.admin(s.handlePlayerStore))
|
||||
mux.HandleFunc(prefix+"/playerstore/deadletters", s.admin(s.handleDeadLetters))
|
||||
mux.HandleFunc(prefix+"/playerstore/deadletters/requeue", s.adminDangerous(httpx.AdminOperationID(http.MethodPost, prefix+"/playerstore/deadletters/requeue"), s.handleDLRequeue))
|
||||
mux.HandleFunc(prefix+"/world", s.admin(s.handleWorld))
|
||||
mux.HandleFunc(prefix+"/playerloops", s.admin(s.handlePlayerLoops))
|
||||
mux.HandleFunc(prefix+"/idempotency", s.admin(s.handleIdempotency))
|
||||
mux.HandleFunc(prefix+"/handlers", s.admin(s.handleHandlers))
|
||||
mux.HandleFunc(prefix+"/profiledb", s.admin(s.handleProfileDB))
|
||||
mux.HandleFunc(prefix+"/mock/player", s.adminDangerous(httpx.AdminOperationID(http.MethodPost, prefix+"/mock/player"), s.handlePlayerMutation))
|
||||
s.registerAdminRoutes(mux)
|
||||
s.registerOpsRoutes(mux, prefix)
|
||||
}
|
||||
|
||||
func (s *Service) handlePlayers(w http.ResponseWriter, _ *http.Request) {
|
||||
if s.players == nil {
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "player module not configured"})
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, s.players.Snapshot())
|
||||
}
|
||||
|
||||
func (s *Service) handleAccounts(w http.ResponseWriter, _ *http.Request) {
|
||||
if s.accounts == nil {
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "account runtime not configured"})
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, s.accounts.Snapshot())
|
||||
}
|
||||
|
||||
func (s *Service) handlePlayerStore(w http.ResponseWriter, _ *http.Request) {
|
||||
if s.players == nil {
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "player module not configured"})
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, s.players.StoreStatus())
|
||||
}
|
||||
|
||||
func (s *Service) handleDeadLetters(w http.ResponseWriter, r *http.Request) {
|
||||
if s.players == nil {
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "player module not configured"})
|
||||
return
|
||||
}
|
||||
if r.Method != http.MethodGet {
|
||||
w.Header().Set("Allow", http.MethodGet)
|
||||
httpx.WriteJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{
|
||||
"dead_letters": s.players.DeadLetters(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) handleDLRequeue(w http.ResponseWriter, r *http.Request) {
|
||||
if s.players == nil {
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "player module not configured"})
|
||||
return
|
||||
}
|
||||
if r.Method != http.MethodPost {
|
||||
w.Header().Set("Allow", http.MethodPost)
|
||||
httpx.WriteJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
||||
return
|
||||
}
|
||||
accountID := strings.TrimSpace(r.URL.Query().Get("account_id"))
|
||||
if accountID == "" {
|
||||
httpx.WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "account_id is required"})
|
||||
return
|
||||
}
|
||||
receipt, duplicate, submitted, err := s.submitOpsCmd(r, "logic.playerstore.deadletter_requeue", accountID, "deadletter requeue", map[string]any{
|
||||
"account_id": accountID,
|
||||
})
|
||||
if writeOpsCmdErr(w, err) {
|
||||
return
|
||||
}
|
||||
if submitted && duplicate {
|
||||
writeOpsDup(w, receipt)
|
||||
return
|
||||
}
|
||||
if !s.players.RequeueDeadLetter(accountID) {
|
||||
if submitted {
|
||||
s.markOpsCmdFail(r.Context(), receipt, errors.New("dead letter not found"))
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "dead letter not found"})
|
||||
return
|
||||
}
|
||||
response := map[string]any{
|
||||
"status": "requeued",
|
||||
"account_id": accountID,
|
||||
}
|
||||
if submitted {
|
||||
receipt = s.markOpsAdminOK(r.Context(), receipt)
|
||||
writeOpsAdminReceipt(w, http.StatusOK, response, receipt)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, response)
|
||||
}
|
||||
|
||||
func (s *Service) handleWorld(w http.ResponseWriter, _ *http.Request) {
|
||||
if s.world == nil {
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "world module not configured"})
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, s.world.Snapshot())
|
||||
}
|
||||
|
||||
func (s *Service) handlePlayerLoops(w http.ResponseWriter, _ *http.Request) {
|
||||
httpx.WriteJSON(w, http.StatusOK, s.playerLoops.Snapshot())
|
||||
}
|
||||
|
||||
func (s *Service) handleIdempotency(w http.ResponseWriter, _ *http.Request) {
|
||||
responseCache := map[string]any{}
|
||||
if s.responses != nil {
|
||||
responseCache = s.responses.Snapshot()
|
||||
}
|
||||
if s.idempotency == nil {
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"response_cache": responseCache})
|
||||
return
|
||||
}
|
||||
snapshot := s.idempotency.Snapshot()
|
||||
snapshot["response_cache"] = responseCache
|
||||
httpx.WriteJSON(w, http.StatusOK, snapshot)
|
||||
}
|
||||
|
||||
func (s *Service) handleHandlers(w http.ResponseWriter, _ *http.Request) {
|
||||
httpx.WriteJSON(w, http.StatusOK, s.dispatcher.Snapshot())
|
||||
}
|
||||
|
||||
func (s *Service) handleProfileDB(w http.ResponseWriter, _ *http.Request) {
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{
|
||||
"fields": player.ProfileFieldDescriptors(),
|
||||
"operations": player.ProfileOperationDescriptors(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) handlePlayerMutation(w http.ResponseWriter, r *http.Request) {
|
||||
if s.players == nil {
|
||||
httpx.WriteJSON(w, http.StatusNotFound, map[string]string{"error": "player module not configured"})
|
||||
return
|
||||
}
|
||||
accountID := strings.TrimSpace(r.URL.Query().Get("account_id"))
|
||||
state := strings.TrimSpace(r.URL.Query().Get("state"))
|
||||
if accountID == "" {
|
||||
httpx.WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "account_id is required"})
|
||||
return
|
||||
}
|
||||
if state == "" {
|
||||
state = "online"
|
||||
}
|
||||
|
||||
receipt, duplicate, submitted, err := s.submitOpsCmd(r, "logic.mock.player_mutation", accountID, "mock player mutation", map[string]any{
|
||||
"account_id": accountID,
|
||||
"state": state,
|
||||
})
|
||||
if writeOpsCmdErr(w, err) {
|
||||
return
|
||||
}
|
||||
if submitted && duplicate {
|
||||
writeOpsDup(w, receipt)
|
||||
return
|
||||
}
|
||||
|
||||
record := s.players.Upsert(accountID, state)
|
||||
event := contracts.PlayerStateChanged{
|
||||
AccountID: record.AccountID,
|
||||
State: record.State,
|
||||
UpdatedAt: record.UpdatedAt,
|
||||
}
|
||||
s.publishAsync(contracts.TopicPlayerStateChange, event)
|
||||
if submitted {
|
||||
receipt = s.markOpsAdminOK(r.Context(), receipt)
|
||||
writeOpsAdminReceipt(w, http.StatusOK, map[string]any{"player": record}, receipt)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, record)
|
||||
}
|
||||
Reference in new issue
Block a user