Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beta54 #126

Merged
merged 66 commits into from
Apr 5, 2024
Merged
Changes from 1 commit
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
1479feb
beta9
Hoshinonyaruko Jan 26, 2024
4a6f523
beta11
Hoshinonyaruko Jan 26, 2024
8739198
beta11
Hoshinonyaruko Jan 26, 2024
d9e0844
beta13
Hoshinonyaruko Jan 26, 2024
ab96bf7
beta14
Hoshinonyaruko Jan 26, 2024
9b7bf0e
beta12
Hoshinonyaruko Jan 26, 2024
7c66e7d
beta13
Hoshinonyaruko Jan 26, 2024
eb0b186
beta15
Hoshinonyaruko Jan 26, 2024
122de8b
beta16
Hoshinonyaruko Jan 26, 2024
5e4903f
beta17
Hoshinonyaruko Jan 26, 2024
7a82cfb
beta18
Hoshinonyaruko Jan 27, 2024
ecc8263
mergefix
Hoshinonyaruko Jan 27, 2024
d0a0a85
beta19
Hoshinonyaruko Jan 27, 2024
afcb856
beta20
Hoshinonyaruko Jan 27, 2024
fafe0f9
beta20
Hoshinonyaruko Jan 27, 2024
397d4c7
beta21
Hoshinonyaruko Jan 27, 2024
c00e1a8
fixmerge
Hoshinonyaruko Jan 27, 2024
42ed202
add_playermanager_api
Hoshinonyaruko Jan 27, 2024
31a6771
beta22
Hoshinonyaruko Jan 27, 2024
050d359
beta22
Hoshinonyaruko Jan 27, 2024
939e818
beta23
Hoshinonyaruko Jan 28, 2024
19cf927
merge
Hoshinonyaruko Jan 28, 2024
8073f3a
beta24
Hoshinonyaruko Jan 29, 2024
fdd6016
mergechanges
Hoshinonyaruko Jan 29, 2024
b167455
beta25
Hoshinonyaruko Jan 30, 2024
844698b
merge
Hoshinonyaruko Jan 30, 2024
f5c8d82
beta26
Hoshinonyaruko Jan 30, 2024
2d5e94d
merge
Hoshinonyaruko Jan 30, 2024
3076085
beta27
Hoshinonyaruko Jan 30, 2024
a8a67ed
beta28
Hoshinonyaruko Jan 31, 2024
4c954fa
beta28
Hoshinonyaruko Jan 31, 2024
2f9063e
beta28
Hoshinonyaruko Jan 31, 2024
cd6fad7
beta29
Hoshinonyaruko Jan 31, 2024
2651176
beta29
Hoshinonyaruko Jan 31, 2024
265b5e3
beta30
Hoshinonyaruko Jan 31, 2024
8aac47a
beta31
Hoshinonyaruko Feb 2, 2024
a559fa2
beta31
Hoshinonyaruko Feb 2, 2024
f5bb66a
beta32
Hoshinonyaruko Feb 2, 2024
44fac8f
beta33
Hoshinonyaruko Feb 6, 2024
a44ef68
merge
Hoshinonyaruko Feb 6, 2024
e731f93
beta34
Hoshinonyaruko Feb 8, 2024
c8921a1
beta34
Hoshinonyaruko Feb 8, 2024
e6b4a3e
beta35
Hoshinonyaruko Feb 8, 2024
ac77292
merge
Hoshinonyaruko Feb 8, 2024
3992e84
beta35
Hoshinonyaruko Feb 8, 2024
d19fc5c
beta36
Hoshinonyaruko Feb 8, 2024
ff271bc
merge
Hoshinonyaruko Feb 8, 2024
a451334
beta37
Hoshinonyaruko Feb 18, 2024
6e4f28c
beta37
Hoshinonyaruko Feb 18, 2024
89d6c06
beta38
Hoshinonyaruko Feb 18, 2024
75002bc
beta39
Hoshinonyaruko Feb 18, 2024
e808a36
beta40
Hoshinonyaruko Feb 18, 2024
d2d5f4c
beta41
Hoshinonyaruko Feb 25, 2024
a40afd7
beta41
Hoshinonyaruko Feb 25, 2024
5977c8d
beat43
Hoshinonyaruko Mar 2, 2024
0ca7960
beta43
Hoshinonyaruko Mar 2, 2024
44b7bb5
beta45
Hoshinonyaruko Mar 2, 2024
d40d897
beta45
Hoshinonyaruko Mar 2, 2024
18282f6
beta46
Hoshinonyaruko Mar 3, 2024
4c89765
beta47
Hoshinonyaruko Mar 3, 2024
881dd1f
beta50
Hoshinonyaruko Mar 3, 2024
b5a8fbd
beta51
Hoshinonyaruko Mar 3, 2024
74bcb7c
beta52
Hoshinonyaruko Mar 3, 2024
4ef420a
beta53
Hoshinonyaruko Mar 3, 2024
159fd6a
Merge branch 'main' of https://github.com/Hoshinonyaruko/palworld-go …
Hoshinonyaruko Apr 5, 2024
dccfad2
beta54
Hoshinonyaruko Apr 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
beta28
Hoshinonyaruko committed Jan 31, 2024
commit a8a67ed88516e62c7aa196d4c5f7aab6ea73cd0f
14 changes: 13 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
@@ -18,6 +18,12 @@ import (
"gopkg.in/ini.v1"
)

type PlayerW struct {
Name string `json:"name"`
SteamID string `json:"steamid"`
PlayerUID string `json:"playeruid"`
}

type Config struct {
GamePath string `json:"gamePath"` // 游戏可执行文件路径PalServer.exe所处的位置
GameSavePath string `json:"gameSavePath"` // 游戏存档路径 \PalServer\Pal\Saved\文件夹的完整路径
@@ -46,6 +52,8 @@ type Config struct {
MaintenanceWarningMessage string `json:"maintenanceWarningMessage"` // 维护警告消息
WorldSettings *GameWorldSettings `json:"worldSettings"` // 帕鲁设定
Engine *Engine `json:"engine"` // 服务端引擎设置
Players []*PlayerW `json:"players"` //白名单玩家数组
WhiteCheckTime int `json:"whiteCheckTime"` //白名单检测时间
}

// 默认配置
@@ -72,9 +80,13 @@ var defaultConfig = Config{
TotalMemoryGB: 16, // 16G
MemoryCleanupInterval: 1800, // 内存清理时间间隔,设为半小时(1800秒)0代表不清理
RestartInterval: 0, // 自动重启间隔
WhiteCheckTime: 0, //白名单检查周期
RegularMessages: []string{"", ""}, // 默认的定期推送消息数组,初始可为空
MessageBroadcastInterval: 3600, // 默认消息广播周期,假设为1小时(3600秒)
MaintenanceWarningMessage: "server is going to rebot,please relogin at 1minute later.", // 默认的维护警告消息
Players: []*PlayerW{
{}, // 一个空的PlayerW
},
}

// Engine 默认配置
@@ -279,7 +291,7 @@ func checkAndSetDefaults(config *Config) bool {
fieldName := typ.Field(i).Name

// 特殊处理RestartInterval字段
if fieldName == "RestartInterval" {
if fieldName == "RestartInterval" || fieldName == "WhiteCheckTime" {
continue
}

86 changes: 77 additions & 9 deletions front/palworld-front/src/pages/IndexView.vue
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
<q-tab name="engine" label="引擎配置修改" />
<q-tab name="command" label="服务器指令" />
<q-tab name="player-manage" label="玩家管理" />
<q-tab name="player-white" label="玩家白名单" />
<q-tab name="advanced" label="SAV修改" @click="redirectToSav" />
<q-tab name="server-check" label="主机管理" />
<q-tab name="save-manage" label="存档管理" />
@@ -583,13 +584,6 @@
label="合作玩家最大数量"
class="q-my-md"
/>
<q-input
filled
v-model.number="config.worldSettings.serverPlayerMaxNum"
type="number"
label="服务器玩家最大数量"
class="q-my-md"
/>

<!-- 开关 -->
<q-toggle
@@ -871,6 +865,70 @@
<q-page padding v-if="tab === 'player-manage'">
<player-manage />
</q-page>
<q-page padding v-if="tab === 'player-white'">
<!-- 白名单配置修改页面内容 -->
<div class="q-gutter-xs q-mt-md">
<div class="text-subtitle2">白名单配置修改</div>
<div class="q-my-md">
<div class="text-h6">白名单玩家</div>
<!-- 保存按钮 -->
<q-btn
color="primary"
label="保存"
@click="saveConfig"
class="q-mt-md"
/>
<q-input
filled
v-model.number="config.whiteCheckTime"
type="number"
label="白名单检查频率 (秒/次)"
hint="检查服务器内是否有不包含白名单的玩家并踢出."
class="q-my-md"
/>
<div
v-for="(player, index) in config.players"
:key="index"
class="q-mb-sm row items-center"
>
<q-input
filled
v-model="player.name"
label="玩家名"
dense
class="col-3 q-mr-md"
/>
<q-input
filled
v-model="player.steamid"
label="SteamID (可选)"
dense
class="col-3 q-mr-md"
/>
<q-input
filled
v-model="player.playeruid"
label="PlayerUID (可选)"
dense
class="col-3 q-mr-md"
/>
<q-btn
flat
icon="delete"
@click="removePlayer(index)"
class="q-ml-md"
/>
</div>
<q-btn
flat
icon="add"
@click="addPlayer"
label="添加玩家"
class="q-mt-md"
/>
</div>
</div>
</q-page>
<q-page padding v-if="tab === 'server-check'">
<div class="text-h6">服务器检测页面</div>
<running-process-status
@@ -904,6 +962,8 @@ const props = defineProps({
uin: Number,
});

const players = ref([]);

const status = ref(null); // 假设 ProcessInfo 是一个对象,这里使用 null 作为初始值

const tab = ref('guard'); // 默认选中守护配置修改
@@ -964,6 +1024,14 @@ const removeMessageServerOptions = (index) => {
config.value.serverOptions.splice(index, 1);
};

function addPlayer() {
config.value.players.push({ name: '', steamid: '', playeruid: '' });
}

function removePlayer(index) {
config.value.players.splice(index, 1);
}

onMounted(async () => {
try {
const response = await axios.get('/api/getjson');
@@ -1059,7 +1127,7 @@ onUnmounted(() => {
const commands2 = [
{ label: '关闭服务器', prefix: 'Shutdown {Seconds} {MessageText}' },
{ label: '强制关闭', prefix: 'DoExit' },
{ label: '广播', prefix: 'Broadcast {MessageText}' },
{ label: '广播', prefix: 'broadcast {MessageText}' },
{ label: '踢人', prefix: 'KickPlayer {SteamID}' },
{ label: '禁止玩家进入', prefix: 'BanPlayer {SteamID}' },
{ label: '传送', prefix: 'TeleportToPlayer {SteamID}' },
@@ -1073,7 +1141,7 @@ const commands2 = [
const commands = [
{ label: '关闭服务器', prefix: 'Shutdown ' },
{ label: '强制关闭', prefix: 'DoExit' },
{ label: '广播', prefix: 'Broadcast ' },
{ label: '广播', prefix: 'broadcast ' },
{ label: '踢人', prefix: 'KickPlayer ' },
{ label: '禁止玩家进入', prefix: 'BanPlayer ' },
{ label: '传送', prefix: 'TeleportToPlayer ' },
14 changes: 14 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -248,6 +248,20 @@ func main() {
}()
}

if jsonconfig.WhiteCheckTime != 0 {
//白名单
whiteInterval := time.Duration(jsonconfig.WhiteCheckTime) * time.Second
whiteTicker := time.NewTicker(whiteInterval)
go func() {
defer whiteTicker.Stop()
for range whiteTicker.C {
fmt.Println("checking player whitelist")
tool.CheckAndKickPlayers(jsonconfig)
}
}()
}

//定时重启
if jsonconfig.RestartInterval != 0 {
restartInterval := time.Duration(jsonconfig.RestartInterval) * time.Second
restartTicker := time.NewTicker(restartInterval)
Binary file modified mod/embeds/PalServerInject.exe
Binary file not shown.
Binary file modified mod/embeds/pal-plugin-loader.dll
Binary file not shown.
26 changes: 23 additions & 3 deletions rconclient.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ package main
import (
"fmt"
"log"
"net/http"
"net/url"

"github.com/gorcon/rcon"
"github.com/hoshinonyaruko/palworld-go/config"
@@ -56,9 +58,27 @@ func HandleMemoryUsage(threshold float64, RconClient *RconClient, config config.
log.Printf("Error broadcasting memory threshold alert: %v", err)
}

//广播自定义内容
if _, err := RconClient.Conn.Execute("broadcast " + config.MaintenanceWarningMessage); err != nil {
log.Printf("Error broadcasting: %v", err)
// 判断是否使用新的DLL方式发送广播
if config.UseDll {
// 构建请求的URL
base := "http://127.0.0.1:53000/rcon?text="
message := "broadcast " + url.QueryEscape(config.MaintenanceWarningMessage)
fullURL := base + message

// 发送HTTP请求
resp, err := http.Get(fullURL)
if err != nil {
log.Printf("Error sending HTTP request: %v", err)
return
}
defer resp.Body.Close()
// 可以添加更多的响应处理逻辑
log.Println("Broadcast message sent successfully via HTTP")
} else {
// 原有的方式发送广播
if _, err := RconClient.Conn.Execute("broadcast " + config.MaintenanceWarningMessage); err != nil {
log.Printf("Error broadcasting: %v", err)
}
}

// 保存游戏状态
92 changes: 77 additions & 15 deletions tool/rcon.go
Original file line number Diff line number Diff line change
@@ -2,8 +2,12 @@
package tool

import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
@@ -117,23 +121,39 @@ func BanPlayer(config config.Config, steamID string) error {
}

func Broadcast(config config.Config, message string) error {
address := config.Address + ":" + strconv.Itoa(config.WorldSettings.RconPort)
exec, err := NewExecutor(address, config.WorldSettings.AdminPassword, true)
if err != nil {
return err
}
defer exec.Close()

message = strings.ReplaceAll(message, " ", "_")
// 如果config.Usedll为true,则使用HTTP方式发送消息
if config.UseDll {
base := "http://127.0.0.1:53000/rcon?text="
messageText := "broadcast " + url.QueryEscape(message)
fullURL := base + messageText

// 发送HTTP请求
resp, err := http.Get(fullURL)
if err != nil {
return err
}
defer resp.Body.Close()
// 这里可以添加对resp的处理逻辑
log.Println("Broadcast message sent successfully via HTTP")
return nil
} else {
// 原始方法发送
address := config.Address + ":" + strconv.Itoa(config.WorldSettings.RconPort)
exec, err := NewExecutor(address, config.WorldSettings.AdminPassword, true)
if err != nil {
return err
}
defer exec.Close()

response, err := exec.Execute("Broadcast " + message)
if err != nil {
return err
}
if response != fmt.Sprintf("Broadcasted: %s", message) {
return errors.New(response)
response, err := exec.Execute("Broadcast " + strings.ReplaceAll(message, " ", "_"))
if err != nil {
return err
}
if response != fmt.Sprintf("Broadcasted: %s", message) {
return errors.New(response)
}
return nil
}
return nil
}

func Shutdown(config config.Config, seconds string, message string) error {
@@ -175,3 +195,45 @@ func DoExit(config config.Config) error {
}
return nil
}

func CheckAndKickPlayers(config config.Config) {
if len(config.Players) == 0 {
return // 白名单为空时不执行操作
}

apiURL := fmt.Sprintf("http://127.0.0.1:%s/api/player?update=true", config.WebuiPort)
resp, err := http.Get(apiURL)
if err != nil {
log.Printf("获取玩家信息失败: %v", err)
return
}
defer resp.Body.Close()

var players []PlayerW
if err := json.NewDecoder(resp.Body).Decode(&players); err != nil {
log.Printf("解析玩家信息失败: %v", err)
return
}

for _, player := range players {
if player.Online && !isPlayerInWhitelist(player, config.Players) {
// 玩家在线但不在白名单,执行踢出操作
if err := KickPlayer(config, player.SteamID); err != nil {
log.Printf("踢出玩家失败: %v", err)
} else {
log.Printf("踢出玩家%v成功: %v", player.Name, err)
}
}
}
}

func isPlayerInWhitelist(player PlayerW, whitelist []*config.PlayerW) bool {
for _, wp := range whitelist {
if (wp.Name == "" || wp.Name == player.Name) &&
(wp.SteamID == "" || wp.SteamID == player.SteamID) &&
(wp.PlayerUID == "" || wp.PlayerUID == player.PlayerUID) {
return true
}
}
return false
}
7 changes: 7 additions & 0 deletions tool/schedule.go
Original file line number Diff line number Diff line change
@@ -19,6 +19,13 @@ type Player struct {
LastOnline time.Time `json:"last_online"`
}

type PlayerW struct {
Name string `json:"name"`
SteamID string `json:"steamid"`
PlayerUID string `json:"playeruid"`
Online bool `json:"online"`
}

func ScheduleTask(db *bbolt.DB, config config.Config) {
ticker := time.NewTicker(3 * time.Minute)
defer ticker.Stop()
35 changes: 30 additions & 5 deletions webui/api.go
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import (
"io"
"log"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
@@ -39,6 +40,7 @@ type Player struct {
SteamID string `json:"steamid"`
PlayerUID string `json:"playeruid"`
LastOnline time.Time `json:"last_online"`
Online bool `json:"online"`
}

type Client struct {
@@ -285,14 +287,37 @@ func (c *Client) readPump(config config.Config) {
log.Println("RCON客户端初始化失败,无法处理webui面板请求,请按教程正确开启rcon和设置服务端admin密码")
return
}
response, err := rconClient.Conn.Execute(string(message))
if err != nil {
log.Printf("RCON execute error: %v", err)
continue
// 检查消息是否以"Broadcast"开头 且注入了DLL 可以使用第三方rcon
if strings.HasPrefix(string(message), "broadcast") && config.UseDll {
// 使用本地方式发送
base := "http://127.0.0.1:53000/rcon?text="
messageText := url.QueryEscape(string(message))
fullURL := base + messageText

// 发送HTTP请求
resp, err := http.Get(fullURL)
if err != nil {
log.Printf("Error sending HTTP request: %v", err)
return
}
defer resp.Body.Close()
// 可以添加更多的响应处理逻辑
log.Println("Message sent successfully via HTTP")
} else {
// 使用原始方式发送
response, err := rconClient.Conn.Execute(string(message))
if err != nil {
log.Printf("Error sending message: %v", err)
}
if err != nil {
log.Printf("RCON execute error: %v", err)
continue
}
c.send <- response
}
c.send <- response
}
}

func (c *Client) writePump() {
defer func() {
c.conn.Close()