Skip to content

Commit

Permalink
Beta24 (#41)
Browse files Browse the repository at this point in the history
* beta9

* beta11

* beta11

* beta13

* beta12

* beta13

* beta15

* beta16

* beta17

* beta18

* beta19

* beta20

* beta21

* add_playermanager_api

* beta22

* beta23

* beta24
  • Loading branch information
Hoshinonyaruko authored Jan 29, 2024
1 parent 50a0854 commit 3783b3c
Show file tree
Hide file tree
Showing 43 changed files with 2,912 additions and 108 deletions.
646 changes: 646 additions & 0 deletions bot/bot.go

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions bot/cookiecheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package bot

import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"log"

"github.com/boltdb/bolt"
)

var (
db *bolt.DB
)

const (
DBName = "bot.db"
BotBucket = "botdata"
)

func InitializeDB() {
var err error
db, err = bolt.Open(DBName, 0600, nil)
if err != nil {
log.Fatalf("Error opening DB: %v", err)
}

db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(BotBucket))
return err
})
}

// CloseDatabase 提供关闭数据库的方法
func CloseDatabase() error {
if db != nil {
return db.Close()
}
return nil
}

// CheckAndWriteCookie 检查cookie是否存在,如果不存在则写入数据库
func CheckAndWriteCookie(value string) (bool, error) {
if db == nil {
return false, errors.New("database not initialized")
}

// 检查键是否存在
exists := false
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(BotBucket))
if b != nil {
v := b.Get([]byte(value))
exists = v != nil
}
return nil
})
if err != nil {
return false, err
}
if exists {
return true, nil
}

// 键不存在,写入新键值对
err = db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(BotBucket))
if err != nil {
return err
}
return b.Put([]byte(value), []byte("true"))
})
return false, err
}

type UserIPData struct {
IP string `json:"ip"`
UUID string `json:"uuid"`
Https bool `json:"https"`
}

// StoreUserIDAndIP 用于存储用户ID(int64)、IP地址(string)和UUID(string)
func StoreUserIDAndIP(userID int64, ip string, uuid string, https bool) error {
if db == nil {
return errors.New("database not initialized")
}

userData := UserIPData{
IP: ip,
UUID: uuid,
Https: https,
}
userDataBytes, err := json.Marshal(userData)
if err != nil {
return err
}

return db.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte("ipuserdata"))
if err != nil {
return err
}
userIDBytes := make([]byte, 8)
binary.BigEndian.PutUint64(userIDBytes, uint64(userID))
return bucket.Put(userIDBytes, userDataBytes)
})
}

// RetrieveIPByUserID 用于通过用户ID(int64)检索IP地址(string)和UUID(string)
func RetrieveIPByUserID(userID int64) (UserIPData, error) {
var userData UserIPData
if db == nil {
return userData, errors.New("database not initialized")
}

err := db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("ipuserdata"))
if bucket == nil {
return fmt.Errorf("bucket %s not found", "ipuserdata")
}
userIDBytes := make([]byte, 8)
binary.BigEndian.PutUint64(userIDBytes, uint64(userID))
value := bucket.Get(userIDBytes)
if value == nil {
return fmt.Errorf("no data found for user ID %d", userID)
}
return json.Unmarshal(value, &userData)
})

if err != nil {
return userData, err
}
return userData, nil
}

type PlayerInfo struct {
PlayerUID string `json:"playerUID"`
SteamID string `json:"steamID"`
Name string `json:"name"`
Online bool `json:"online"`
LastOnline string `json:"last_online"`
}

// StorePlayerInfo 存储玩家信息并返回唯一ID
func StorePlayerInfo(playerUID, steamID, name string) (int64, error) {
if db == nil {
return 0, errors.New("database not initialized")
}

playerInfo := PlayerInfo{
PlayerUID: playerUID,
SteamID: steamID,
Name: name,
}

var id int64
err := db.Update(func(tx *bolt.Tx) error {
// 创建或获取PlayerList bucket
bucket, err := tx.CreateBucketIfNotExists([]byte("playerlist"))
if err != nil {
return err
}

// 检查是否已存在相同的玩家信息
c := bucket.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
var existingPlayerInfo PlayerInfo
if err := json.Unmarshal(v, &existingPlayerInfo); err != nil {
continue
}
if existingPlayerInfo.PlayerUID == playerUID && existingPlayerInfo.SteamID == steamID && existingPlayerInfo.Name == name {
// 解析ID
id = int64(binary.BigEndian.Uint64(k))
return nil
}
}

// 分配新的唯一ID
seq, _ := bucket.NextSequence()
id = int64(seq) // 转换为 int64
idBytes := make([]byte, 8)
binary.BigEndian.PutUint64(idBytes, uint64(id))
playerInfoBytes, err := json.Marshal(playerInfo)
if err != nil {
return err
}
return bucket.Put(idBytes, playerInfoBytes)
})

return id, err
}

// RetrievePlayerInfoByID 通过ID检索玩家信息
func RetrievePlayerInfoByID(id int64) (PlayerInfo, error) {
var playerInfo PlayerInfo
if db == nil {
return playerInfo, errors.New("database not initialized")
}

err := db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("playerlist"))
if bucket == nil {
return fmt.Errorf("bucket %s not found", "playerlist")
}
idBytes := make([]byte, 8)
binary.BigEndian.PutUint64(idBytes, uint64(id))
value := bucket.Get(idBytes)
if value == nil {
return fmt.Errorf("no player found for ID %d", id)
}
return json.Unmarshal(value, &playerInfo)
})

return playerInfo, err
}
78 changes: 78 additions & 0 deletions bot/ip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package bot

import (
"fmt"
"strconv"
"strings"
)

func ipToNumber(ip string) (int64, error) {
parts := strings.Split(ip, ".")
if len(parts) != 4 {
return 0, fmt.Errorf("invalid IP address format")
}

r, err := strconv.Atoi(parts[0])
if err != nil {
return 0, err
}
g, err := strconv.Atoi(parts[1])
if err != nil {
return 0, err
}
b, err := strconv.Atoi(parts[2])
if err != nil {
return 0, err
}
n, err := strconv.Atoi(parts[3])
if err != nil {
return 0, err
}

c := int64(r) + int64(g)*256 + int64(b)*256*256 + int64(n)*256*256*256
c = (c + 250) * 2
return c, nil
}

func numberToIP(num int64) string {
// 因为在转换时执行了 (c + 250) * 2
// 所以先进行反向操作
num = (num / 2) - 250

// 提取IP的四个部分
n := num / (256 * 256 * 256) % 256
b := num / (256 * 256) % 256
g := num / 256 % 256
r := num % 256

// 构造IP地址
return fmt.Sprintf("%d.%d.%d.%d", r, g, b, n)
}

func IpToNumberWithPort(ip string) (int64, error) {
parts := strings.Split(ip, ":")
if len(parts) != 2 {
return 0, fmt.Errorf("invalid IP address and port format")
}

ipPart, portPart := parts[0], parts[1]
port, err := strconv.Atoi(portPart)
if err != nil {
return 0, fmt.Errorf("invalid port: %s", err)
}

num, err := ipToNumber(ipPart)
if err != nil {
return 0, err
}

return num*65536 + int64(port), nil // 65536 = 256*256,确保端口不会与IP地址冲突
}

func numberToIPWithPort(num int64) string {
port := num % 65536
num = num / 65536

ip := numberToIP(num)
return fmt.Sprintf("%s:%d", ip, port)
}
12 changes: 12 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ type Config struct {
GamePath string `json:"gamePath"` // 游戏可执行文件路径PalServer.exe所处的位置
GameSavePath string `json:"gameSavePath"` // 游戏存档路径 \PalServer\Pal\Saved\文件夹的完整路径
BackupPath string `json:"backupPath"` // 备份路径
SteamPath string `json:"steamPath"` // steam路径
CommunityServer bool `json:"communityServer"` // 社区服务器开关
UseDll bool `json:"useDll"` // dll注入
Cert string `json:"cert"` // 证书
Key string `json:"key"` // 密钥
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
Onebotv11HttpApiPath string `json:"onebotV11HttpApiPath"` // 机器人框架api地址
ServerOptions []string `json:"serverOptions"` // 服务器启动参数
CheckInterval int `json:"checkInterval"` // 进程存活检查时间(秒)
BackupInterval int `json:"backupInterval"` // 备份间隔(秒)
Expand All @@ -43,9 +49,15 @@ var defaultConfig = Config{
GamePath: "",
GameSavePath: "",
BackupPath: "",
SteamPath: "",
CommunityServer: false,
Address: "127.0.0.1",
UseHttps: false,
ProcessName: "PalServer",
Onebotv11HttpApiPath: "",
UseDll: false,
Cert: "",
Key: "",
ServerOptions: []string{"-useperfthreads", "-NoAsyncLoadingThread", "-UseMultithreadForDS"},
CheckInterval: 30, // 30 秒
WebuiPort: "52000", // Webui 端口号
Expand Down
52 changes: 52 additions & 0 deletions front/palworld-front/src/components/BotManage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<div class="my-page">
<q-btn color="blue" label="生成机器人指令" @click="getBot"></q-btn>
<q-btn color="primary" label="获取机器人" @click="getBotLink"></q-btn>
<q-input v-model="apiResponse" readonly></q-input>
<q-btn color="green" label="点击复制" @click="copyResponse"></q-btn>
<div>如果点击复制无效,请在守护设置开启强制https或手动复制。</div>
<div>指令包含加密后的你的面板地址,需放通webui端口到公网。</div>
<div>建议发出给帕鲁帕鲁后立即撤回。</div>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { QBtn, QInput } from 'quasar';
const apiResponse = ref('');
const getBot = async () => {
const response = await fetch('/api/getbot', {
method: 'POST',
credentials: 'include',
});
apiResponse.value = await response.text();
};
const getBotLink = async () => {
const response = await fetch('/api/getbotlink', {
method: 'POST',
credentials: 'include',
});
apiResponse.value = await response.text();
};
const copyResponse = () => {
navigator.clipboard.writeText(apiResponse.value).catch(() => {
console.error('复制失败,请手动复制');
});
};
</script>

<style lang="scss">
.my-page {
.q-btn {
margin-bottom: 10px;
}
.q-input {
margin-bottom: 10px;
}
}
</style>
Loading

0 comments on commit 3783b3c

Please sign in to comment.