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

Beta23 #33

Merged
merged 22 commits into from
Jan 28, 2024
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Config struct {
GameSavePath string `json:"gameSavePath"` // 游戏存档路径 \PalServer\Pal\Saved\文件夹的完整路径
BackupPath string `json:"backupPath"` // 备份路径
Address string `json:"address"` // 服务器 IP 地址
UseHttps bool `json:"usehttps"` // 使用 https
WebuiPort string `json:"webuiPort"` // Webui 端口号
AutolaunchWebui bool `json:"autoLaunchWebui"` // 自动打开webui
ProcessName string `json:"processName"` // 进程名称 PalServer
Expand All @@ -43,6 +44,7 @@ var defaultConfig = Config{
GameSavePath: "",
BackupPath: "",
Address: "127.0.0.1",
UseHttps: false,
ProcessName: "PalServer",
ServerOptions: []string{"-useperfthreads", "-NoAsyncLoadingThread", "-UseMultithreadForDS"},
CheckInterval: 30, // 30 秒
Expand Down
2 changes: 1 addition & 1 deletion front/palworld-front/quasar.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ module.exports = configure(function (/* ctx */) {
// directives: [],

// Quasar plugins
plugins: [],
plugins: ['Dialog', 'Notify'],
},

// animations: 'all', // --- includes all animations
Expand Down
21 changes: 21 additions & 0 deletions front/palworld-front/src/components/RunningProcessStatus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,29 @@
<strong>磁盘使用率:</strong>
<code>{{ status?.disk.percent.toFixed(2) }}%</code>
</q-chip>
<div class="q-mt-lg">
<q-btn color="green" label="重启服务端" @click="restartServer"></q-btn>
<q-btn color="blue" label="开启服务端" @click="startServer"></q-btn>
<q-btn color="orange" label="关闭服务端" @click="stopServer"></q-btn>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
import axios from 'axios';

const props = defineProps<{ status: any }>();

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on linux for arm64

'props' is assigned a value but never used

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on linux for arm64

Unexpected any. Specify a different type

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for amd64

'props' is assigned a value but never used

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for amd64

Unexpected any. Specify a different type

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on windows for amd64

'props' is assigned a value but never used

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on windows for amd64

Unexpected any. Specify a different type

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on linux for amd64

'props' is assigned a value but never used

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on linux for amd64

Unexpected any. Specify a different type

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for arm64

'props' is assigned a value but never used

Check warning on line 49 in front/palworld-front/src/components/RunningProcessStatus.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for arm64

Unexpected any. Specify a different type

const sendRequest = async (url: string) => {
try {
const response = await axios.post(url);
console.log('Response:', response);
} catch (error) {
console.error('Error:', error);
}
};

const restartServer = () => sendRequest('/api/restart');
const startServer = () => sendRequest('/api/start');
const stopServer = () => sendRequest('/api/stop');
</script>
106 changes: 106 additions & 0 deletions front/palworld-front/src/components/SaveManage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<template>
<q-page padding>
<div class="q-mb-md">
<q-btn label="刷新" color="primary" @click="fetchSaveList" />
<q-btn label="立即保存" color="primary" @click="saveNow" />
</div>

<q-list bordered>
<q-item v-for="save in saveList" :key="save" clickable>
<template v-slot:prepend>
<q-checkbox v-model="selectedSaves" :value="save" />
</template>
<q-item-section>{{ save }}</q-item-section>
<q-item-section side>
<q-btn flat @click="confirmRestore(save)"
><q-icon name="restore" /> 回档到此刻</q-btn
>
</q-item-section>
</q-item>
</q-list>
</q-page>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import {
QPage,
QList,
QItem,
QCheckbox,
QItemSection,
QBtn,
QIcon,
} from 'quasar';
import axios from 'axios';
import { useQuasar } from 'quasar';

const $q = useQuasar();
const saveList = ref<string[]>([]);
const selectedSaves = ref<string[]>([]);

const fetchSaveList = async () => {
try {
const response = await axios.get('/api/getsavelist');
saveList.value = response.data;
} catch (error) {
console.error(error);
}
};

const confirmRestore = (saveName: string) => {
$q.dialog({
title: '确认',
message: `您确定要回档到 "${saveName}" 吗?`,
cancel: true,
persistent: true,
}).onOk(() => restoreSave(saveName));
};

const restoreSave = async (saveName: string) => {
try {
const response = await axios.post('/api/changesave', { path: saveName });
if (response.status === 200) {
$q.notify({
color: 'green',
textColor: 'white',
icon: 'cloud_done',
message: '回档成功',
});
}
} catch (error) {
console.error(error);
$q.notify({
color: 'red',
textColor: 'white',
icon: 'error',
message: '回档失败',
});
}
};

const saveNow = async () => {
// 获取当前时间的 Unix 时间戳(秒)
const timestamp = Math.floor(Date.now() / 1000);

try {
await axios.post('/api/savenow', { timestamp }, { withCredentials: true });
} catch (error) {
console.error(error);
}
};

const deleteSaves = async () => {

Check warning on line 93 in front/palworld-front/src/components/SaveManage.vue

View workflow job for this annotation

GitHub Actions / Build on linux for arm64

'deleteSaves' is assigned a value but never used

Check warning on line 93 in front/palworld-front/src/components/SaveManage.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for amd64

'deleteSaves' is assigned a value but never used

Check warning on line 93 in front/palworld-front/src/components/SaveManage.vue

View workflow job for this annotation

GitHub Actions / Build on windows for amd64

'deleteSaves' is assigned a value but never used

Check warning on line 93 in front/palworld-front/src/components/SaveManage.vue

View workflow job for this annotation

GitHub Actions / Build on linux for amd64

'deleteSaves' is assigned a value but never used

Check warning on line 93 in front/palworld-front/src/components/SaveManage.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for arm64

'deleteSaves' is assigned a value but never used
try {
await axios.post('/api/delsave', { saves: selectedSaves.value });
} catch (error) {
console.error(error);
}
};

onMounted(fetchSaveList);
</script>

<style scoped lang="scss">
// Custom styles here
</style>
22 changes: 20 additions & 2 deletions front/palworld-front/src/pages/IndexView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
<q-tab name="server" label="服务端配置修改" />
<q-tab name="command" label="服务器指令" />
<q-tab name="player-manage" label="玩家管理" />
<q-tab name="advanced" label="高级配置修改" @click="redirectToSav" />
<q-tab name="server-check" label="服务器检测" />
<q-tab name="advanced" label="SAV修改" @click="redirectToSav" />
<q-tab name="server-check" label="主机管理" />
<q-tab name="save-manage" label="存档管理" />
</q-tabs>
</q-header>

Expand All @@ -33,6 +34,11 @@
label="自动打开 Web UI"
class="q-my-md"
/>
<q-toggle
v-model="config.usehttps"
label="webui强制https(解锁玩家管理复制按钮)"
class="q-my-md"
/>
<q-input
filled
v-model="config.maintenanceWarningMessage"
Expand Down Expand Up @@ -283,6 +289,13 @@
/>

<!-- 数字输入框 -->
<q-input
filled
v-model.number="config.worldSettings.serverPlayerMaxNum"
type="number"
label="游戏服务器最大人数"
class="q-my-md"
/>
<q-input
filled
v-model.number="config.worldSettings.publicPort"
Expand Down Expand Up @@ -639,6 +652,10 @@
:status="status"
/>
</q-page>
<!-- 存档管理组件 -->
<q-page padding v-if="tab === 'save-manage'">
<save-manage />
</q-page>
</q-page-container>
</q-layout>
</template>
Expand All @@ -649,6 +666,7 @@
import { QPage, QCard, QCardSection } from 'quasar';
import RunningProcessStatus from 'components/RunningProcessStatus.vue';
import PlayerManage from 'components/PlayerManage.vue';
import SaveManage from 'components/SaveManage.vue';

//给components传递数据
const props = defineProps({
Expand All @@ -668,11 +686,11 @@
const showDifficultyTooltip = ref(false);
const showDeathPenaltyTooltip = ref(false);

const toggleTooltip = (type) => {

Check warning on line 689 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on linux for arm64

'type' is defined but never used

Check warning on line 689 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for amd64

'type' is defined but never used

Check warning on line 689 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on windows for amd64

'type' is defined but never used

Check warning on line 689 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on linux for amd64

'type' is defined but never used

Check warning on line 689 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for arm64

'type' is defined but never used
showDeathPenaltyTooltip.value = !showDeathPenaltyTooltip.value;
};

const toggleTooltip2 = (type) => {

Check warning on line 693 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on linux for arm64

'type' is defined but never used

Check warning on line 693 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for amd64

'type' is defined but never used

Check warning on line 693 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on windows for amd64

'type' is defined but never used

Check warning on line 693 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on linux for amd64

'type' is defined but never used

Check warning on line 693 in front/palworld-front/src/pages/IndexView.vue

View workflow job for this annotation

GitHub Actions / Build on darwin for arm64

'type' is defined but never used
showDifficultyTooltip.value = !showDifficultyTooltip.value;
};

Expand Down
114 changes: 102 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package main

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"embed"
"encoding/pem"
"fmt"
"io/fs"
"log"
"math/big"
"net"
"net/http"
"os"
"os/exec"
Expand Down Expand Up @@ -38,10 +46,15 @@ func main() {
fmt.Printf("作者 早苗狐 答疑群:587997911\n")
//给程序整个标题
sys.SetTitle("作者 早苗狐 答疑群:587997911")

// 设置监控和自动重启
supervisor := NewSupervisor(jsonconfig)
go supervisor.Start()

// 设置备份任务
backupTask := NewBackupTask(jsonconfig)
go backupTask.Schedule()

if !supervisor.isServiceRunning() {
supervisor.restartService()
} else {
Expand All @@ -67,22 +80,89 @@ func main() {
webuiGroup.DELETE("/*filepath", webui.CombinedMiddleware(jsonconfig, db))
webuiGroup.PATCH("/*filepath", webui.CombinedMiddleware(jsonconfig, db))
}
// 创建一个http.Server实例(主服务器)

if jsonconfig.UseHttps {
//创造自签名证书
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
randomOrg := generateRandomString(10)
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Palworld-go-" + randomOrg},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),

KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
publicip, err := sys.GetPublicIP()
if err != nil {
fmt.Println("获取当前地址生成https证书失败")
}
ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP(publicip)}
template.IPAddresses = append(template.IPAddresses, ipAddresses...)

derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
panic(err)
}

certOut, err := os.Create("cert.pem")
if err != nil {
panic(err)
}
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()

// 编码 ECDSA 私钥
encodedKey, err := x509.MarshalECPrivateKey(priv)
if err != nil {
panic(err) // 或适当的错误处理
}

// 创建 PEM 文件
keyOut, err := os.Create("key.pem")
if err != nil {
panic(err) // 或适当的错误处理
}
defer keyOut.Close() // 确保文件被正确关闭

// 将编码后的私钥写入 PEM 文件
if err := pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: encodedKey}); err != nil {
panic(err) // 或适当的错误处理
}
}

// 创建一个http.Server实例(主服务器)
httpServer := &http.Server{
Addr: "0.0.0.0:" + jsonconfig.WebuiPort,
Handler: r,
}
fmt.Printf("webui-api运行在%v端口\n", jsonconfig.WebuiPort)
// 在一个新的goroutine中启动主服务器
go func() {
// 使用HTTP
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 设置备份任务
backupTask := NewBackupTask(jsonconfig)
go backupTask.Schedule()

if jsonconfig.UseHttps {
fmt.Printf("webui-api运行在 HTTPS 端口 %v\n", jsonconfig.WebuiPort)
// 在一个新的goroutine中启动主服务器
go func() {
// 使用 HTTPS
if err := httpServer.ListenAndServeTLS("cert.pem", "key.pem"); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
} else {
fmt.Printf("webui-api运行在 HTTP 端口 %v\n", jsonconfig.WebuiPort)
// 在一个新的goroutine中启动主服务器
go func() {
// 使用HTTP
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
}

// 设置推送任务
palworldBroadcast := NewpalworldBroadcast(jsonconfig)
Expand Down Expand Up @@ -218,3 +298,13 @@ func OpenWebUI(config *config.Config) error {

return cmd.Start()
}

func generateRandomString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
randInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
b[i] = letters[randInt.Int64()]
}
return string(b)
}
15 changes: 13 additions & 2 deletions supervisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,23 @@ func (s *Supervisor) restartService() {

if runtime.GOOS == "windows" {
exePath = filepath.Join(s.Config.GamePath, s.Config.ProcessName+".exe")
args = []string{
"-RconEnabled=True",
fmt.Sprintf("-AdminPassword=%s", s.Config.WorldSettings.AdminPassword),
fmt.Sprintf("-port=%d", s.Config.WorldSettings.PublicPort),
fmt.Sprintf("-players=%d", s.Config.WorldSettings.ServerPlayerMaxNum),
}
} else {
exePath = filepath.Join(s.Config.GamePath, s.Config.ProcessName+".sh")
args = []string{
"--RconEnabled=True",
fmt.Sprintf("--AdminPassword=%s", s.Config.WorldSettings.AdminPassword),
fmt.Sprintf("--port=%d", s.Config.WorldSettings.PublicPort),
fmt.Sprintf("--players=%d", s.Config.WorldSettings.ServerPlayerMaxNum),
}
}

args = s.Config.ServerOptions
//args = append(args, gameArgs...) // 添加GameWorldSettings参数
args = append(args, s.Config.ServerOptions...) // 添加GameWorldSettings参数

// 执行启动命令
log.Printf("启动命令: %s %s", exePath, strings.Join(args, " "))
Expand Down
Loading
Loading