Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5867ec6
feat(limitSimplayerNum.ts): 制作了基本的UI框架
kzyqq00-Player Sep 1, 2025
1489148
fix: 不管输入什么都会报错、有__global__玩家名不会报错
kzyqq00-Player Sep 1, 2025
dd7dbb1
refactor(limitSimplayerNum.ts): 优化按钮,使其不是白色还是黑色,从而更好的观察
kzyqq00-Player Sep 1, 2025
b80a7c0
fix: 玩家名显示undefined
kzyqq00-Player Sep 1, 2025
a420abf
abaaba
kzyqq00-Player Sep 1, 2025
fc3bf28
refactor: 不使用没有标准的函数内函数声明,而是使用变量式函数
kzyqq00-Player Sep 1, 2025
0ef813a
feat: 实现功能
kzyqq00-Player Sep 1, 2025
1df343d
fix(limitSimplayerNum.ts): 写反了woc
kzyqq00-Player Sep 1, 2025
0bb35e5
refactor(limitSimplayerNum.ts): 增加命令关键词
kzyqq00-Player Sep 1, 2025
f76f61c
refactor(limitSimplayerNum.ts): 调整用语
kzyqq00-Player Sep 1, 2025
fa762b7
refactor(limitSimplayerNum.ts): 调整用语
kzyqq00-Player Sep 1, 2025
c8fa8fe
docs(README): 更新readme
kzyqq00-Player Sep 2, 2025
9b3191a
refactor(limitSimplayerNum.ts): 增加命令关键词
kzyqq00-Player Sep 2, 2025
f1a0489
refactor: 同步主分支
kzyqq00-Player Sep 2, 2025
4c1bdf6
Merge branch 'main' into limit-simplayer-number
kzyqq00-Player Sep 2, 2025
ebe5db4
refactor: 取消硬编码
kzyqq00-Player Sep 2, 2025
bddb8cd
refactor(youAreMine): 入 乡 随 俗
kzyqq00-Player Sep 2, 2025
1e6feaa
fix: 修复了神秘代码导致的bug
kzyqq00-Player Sep 2, 2025
3c5f43e
fix: 修复了神秘代码导致的bug
kzyqq00-Player Sep 2, 2025
09977f9
fix(limitSimplayerNum.ts): 修复如果配置不是对象就不会报错的bug
kzyqq00-Player Sep 6, 2025
8b19046
refactor(limitSimplayerNum.ts): 判断是否是NaN换成是否是安全整数
kzyqq00-Player Sep 6, 2025
efafeae
fix(limitSimplayerNum.ts): 使用负数会导致依然正常生成
kzyqq00-Player Sep 6, 2025
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
61 changes: 32 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
1. **安装插件**:
- 直接打开 `.mcpack` 文件,Minecraft 将自动完成安装。
- 在创建新世界或编辑旧世界时,在“行为包”选项中激活本插件。
- **务必**在“实验”选项中,开启“**测试版 API**”开关。
- **务必**在创建存档时打开“实验性游戏内容”选项中的“**测试版 API**”开关。

2. **创建假人**:
- 在聊天栏输入 `假人创建` 即可在你的位置创建一个假人。
Expand All @@ -48,54 +48,57 @@

所有指令都以 `假人` 作为开头。

可以使用`/scriptevent`命令调用,前缀是`ffpp`或`ffp`,例如`/scriptevent ffpp:ffpp`。

---

### **基础指令**

| 指令 | 别名 | 功能描述 |
| :--- | :--- | :--- |
| 指令 | 别名 | 功能描述 |
|:---------------|:-------------|:-----------------------|
| `创建 [名称] [坐标]` | `生成`, `ffpp` | 创建一个假人。支持指定名称、坐标和批量创建。 |
| `销毁 [序号]` | `移除`, `清除` | 回收物品并彻底移除一个假人。可指定序号。 |
| `列表` | | 显示当前所有假人的序号和名称。 |
| `帮助 [具体指令]` | `help` | 显示帮助信息。 |
| `github` | | 在聊天框显示项目的GitHub仓库二维码。 |
| `销毁 [序号]` | `移除`, `清除` | 回收物品并彻底移除一个假人。可指定序号。 |
| `列表` | | 显示当前所有假人的序号和名称。 |
| `帮助 [具体指令]` | `help` | 显示帮助信息。 |
| `github` | | 在聊天框显示项目的GitHub仓库二维码。 |

---

### **交互指令**

| 指令 | 别名 | 功能描述 |
| :--- | :--- | :--- |
| `背包交换` | `交换背包` | 与准星对准的假人交换整个背包的物品。 |
| `装备交换` | `交换装备` | 与准星对准的假人交换所有穿戴的装备。 |
| `资源回收` | `背包清空`, `爆金币` | 清空假人背包和装备,将物品掉落,经验返还给你。 |
| `改名 <新名称>` | `重命名` | 为准星对准的假人修改名称。 |
| `重生 [序号]` | `复活` | 复活一个已死亡的假人。可指定序号。 |
| `位置 [序号]` | `坐标` | 获取一个假人当前所在的坐标和维度。 |
| 指令 | 别名 | 功能描述 |
|:-----------|:--------------|:------------------------|
| `背包交换` | `交换背包` | 与准星对准的假人交换整个背包的物品。 |
| `装备交换` | `交换装备` | 与准星对准的假人交换所有穿戴的装备。 |
| `资源回收` | `背包清空`, `爆金币` | 清空假人背包和装备,将物品掉落,经验返还给你。 |
| `改名 <新名称>` | `重命名` | 为准星对准的假人修改名称。 |
| `重生 [序号]` | `复活` | 复活一个已死亡的假人。可指定序号。 |
| `位置 [序号]` | `坐标` | 获取一个假人当前所在的坐标和维度。 |

---

### **行为控制**

| 指令 | 别名 | 功能描述 |
| :--- | :--- | :--- |
| `攻击` | | 让假人进行一次攻击。 |
| `自动攻击` | | 切换“自动攻击”状态(持续攻击前方实体)。 |
| `自动追击` | | 切换“自动追击”状态(自动寻找并攻击敌对目标)。 |
| `挖掘` | `摧毁` | 切换“自动挖掘”状态(持续破坏前方方块)。 |
| `自动丢三叉戟`| | 切换“自动使用”状态(持续使用主手物品)。 |
| `开始跳跃` | | 让假人开始不停地跳跃。 |
| `结束跳跃` | | 让假人停止跳跃。 |
| `停止` | `开摆` | 停止假人当前的所有活动。 |
| 指令 | 别名 | 功能描述 |
|:---------|:-----|:-------------------------|
| `攻击` | | 让假人进行一次攻击。 |
| `自动攻击` | | 切换“自动攻击”状态(持续攻击前方实体)。 |
| `自动追击` | | 切换“自动追击”状态(自动寻找并攻击敌对目标)。 |
| `挖掘` | `摧毁` | 切换“自动挖掘”状态(持续破坏前方方块)。 |
| `自动丢三叉戟` | | 切换“自动使用”状态(持续使用主手物品)。 |
| `开始跳跃` | | 让假人开始不停地跳跃。 |
| `结束跳跃` | | 让假人停止跳跃。 |
| `停止` | `开摆` | 停止假人当前的所有活动。 |

---

### **管理指令**

| 指令 | 别名 | 功能描述 |
| :--- | :--- | :--- |
| `重置序号` | `编号重置` | 重置内部的假人ID计数器。 |
| `命令列表` | `showshowway` | 显示所有已注册的可用指令。 |
| 指令 | 别名 | 功能描述 |
|:-------|:--------------|:-------------------------------------------------------------------------------|
| `重置序号` | `编号重置` | 重置内部的假人ID计数器。 |
| `命令列表` | `showshowway` | 显示所有已注册的可用指令。 |
| `限额配置` | `xepg` | 限制每个玩家或全局的假人数量限制,即限额。<br/>由于<s>`Bugjump`</s>的原因,目前无法通过聊天触发,需要使用`/scriptevent`。 |


## ⚠️ 注意事项
Expand Down
8 changes: 4 additions & 4 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"format_version": 2,
"header": {
"name": "§t1.21.71 v31 §e§lFlash§fFakePlayerPack",
"description": "【保存背包状态-构建于1.21.100-支持1.21.7x-1.21.10x】1.21.71 \n请开启实验性游戏内容(测试版 API)\n感谢PuppyOne和kzyqq00-Player做出的长达数月的代码更新",
"name": "§t1.21.71 v35 §e§lFlash§fFakePlayerPack",
"description": "【保存背包状态-每人限额5人-构建于1.21.100-支持1.21.7x-1.21.10x】1.21.71 \n请开启实验性游戏内容(测试版 API)\n感谢PuppyOne和kzyqq00-Player做出的长达数月的代码更新",
"uuid": "aa101e99-abb4-448d-b58f-71e9da43064e",
"version": [
1,
21,
741
745
],
"min_engine_version": [
1,
Expand All @@ -20,7 +20,7 @@
"version": [
1,
21,
741
745
],
"type": "script",
"uuid": "10101e99-abc1-5488-ba76-71e9da441300",
Expand Down
2 changes: 1 addition & 1 deletion tscripts/lib/xboyPackage/YumeSignEnum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const BEHAVIOR_FUNCTION = {
// commandManager.executeCommand('假人改名', [name], { entity: player, sim });
},
recycle : (sim:SimulatedPlayer,player:Player)=>commandManager.execute('假人资源回收',{entity:player,sim}), // item and exp
disconnect : (sim:SimulatedPlayer)=>commandManager.execute('假人销毁',{sim}),
disconnect : (sim:SimulatedPlayer,player:Player)=>commandManager.execute('假人销毁',{sim,entity:player}),
}
export const exeBehavior = (behavior: string) => BEHAVIOR[behavior] && BEHAVIOR_FUNCTION[behavior]

4 changes: 2 additions & 2 deletions tscripts/lib/yumeCommand/scriptEventHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {
cannotHandledExceptionWarningText
} from "./CommandRegistry";

const namespaces = ['ffpp'];
const namespaces = ['ffp', 'ffpp'];

type ScriptEventID = `ffpp:${string}`;
type ScriptEventID = `ffp:${string}` | `ffpp:${string}`;

class CannotGetLocationError extends Error {
constructor(message: string) {
Expand Down
91 changes: 11 additions & 80 deletions tscripts/xTerrain/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
spawnedEvent,
spawnedEventSignal,
} from '../@types/globalThis'
import { Dimension, LocationOutOfWorldBoundariesError, Player, system, Vector3 } from '@minecraft/server'
import { Dimension, LocationOutOfWorldBoundariesError, system, Vector3 } from '@minecraft/server'

import { register } from '@minecraft/server-gametest'

Expand All @@ -16,7 +16,7 @@ import { SIGN } from '../lib/xboyPackage/YumeSignEnum'
import { world } from '@minecraft/server'

// import './plugins/noFlashDoor' // pig

// THIS IS A SHIT!!! MEI YOU ZHI YI!!!

import './plugins/Backpack2Barrel'
import './plugins/test'
Expand All @@ -30,6 +30,7 @@ import './plugins/task'
import './plugins/gui'
import './plugins/autoFishing'
import './plugins/killedBySimPlayer'
import './plugins/limitSimplayerNum'
// import './plugins/setting'
import './plugins/showCommandsList'
import { playerMove } from "../lib/xboyEvents/move";
Expand All @@ -38,7 +39,7 @@ import '../lib/yumeCommand/scriptEventHandler'

const overworld = world.getDimension('overworld')
const tickWaitTimes = 20 * 60 * 60 * 24 * 365
const max_simulatedPlayerCount = 5
// const max_simulatedPlayerCount = 5
// all of SimulatedPlayer List
export const simulatedPlayers = {}

Expand All @@ -62,88 +63,16 @@ let doMobSpawning = true
// ?

let spawnSimulatedPlayerByNameTag: (location: Vector3, dimension: Dimension, nameTag: string) => SimulatedPlayer
let spawnSimulatedPlayer : (location:Vector3, dimension:Dimension, pid: number )=>SimulatedPlayer
let testWorldLocation: Vector3
let testWorldDimension: Dimension

let currentPID = 0
const GetPID = ()=> ++currentPID

if (!world.structureManager.get('xboyMinemcSIM:void'))
world.structureManager.createEmpty('xboyMinemcSIM:void', { x: 1, y: 1, z: 1 }).saveToWorld()

const GetGlobalPID = () => world.scoreboard.getObjective('##FlashPlayer##').addScore('##currentPID', 1)

// 新增每个玩家的个人假人控制ID
// 使用一个动态属性来管理每个玩家的个人假人控制ID
// 为了一致所以暂不全部迁移到动态属性
const currentPID4EveryPlayer = new Map<string, number>()
const GetCurrentPID4Player = (playerId: string): number | null => {
const _max_simulatedPlayerCount = GetPlayerSimulatedPlayerCount(playerId)
if (_max_simulatedPlayerCount <= 0) return null
const value = currentPID4EveryPlayer.get(playerId) ?? 0

// 获取此玩家当前可用假人数量
const valid_CurrentPID_List = []
for (let i = 1; i <= _max_simulatedPlayerCount; i++) {
const PID = GetPlayerPID2GlobalPID(playerId, i)
if (!PID) { valid_CurrentPID_List.push(i); continue }
if (!simulatedPlayers[PID]?.isValid) { valid_CurrentPID_List.push(i); continue }
}


if (valid_CurrentPID_List.length <= 0) return null
return currentPID4EveryPlayer.set(playerId, value + 1).get(playerId)
}
// 将这个这个每次重启游戏重置为1的玩家假人PID和全局递增PID挂钩并且存储在动态属性以供持久化假人信息
// 每次玩家请求一个假人都会分配一个玩家个人假人PID
// 如果查询到这个玩家的个人假人PID和全局挂钩则将此个人假人PID指向此全局PID以关联持久化信息
// 否则获取一个新的全局PID关联到此假人并且存储到动态属性
// simulatedPlayers中依旧使用全局PID
function SetPlayerPID2GlobalPID(playerId: string, CurrentPID4Player: number, GlobalPID: number) {
// const ex = {
// "-56789": {
// "GlobalPID": 1234567890
// }
// }
// 苦一苦存储,骂名我来担
let data = <string>world.getDynamicProperty("info4EveryPlayer_" + playerId) ?? "{}"
const dataObject = JSON.parse(data)
if (!dataObject[CurrentPID4Player])
dataObject[CurrentPID4Player] = {}
dataObject[CurrentPID4Player]["GlobalPID"] = GlobalPID
data = JSON.stringify(dataObject)
world.setDynamicProperty("info4EveryPlayer_" + playerId, data)
}
const GetPlayerPID2GlobalPID = (playerId: string, CurrentPID4Player: number): number => {
const data = <string>world.getDynamicProperty("info4EveryPlayer_" + playerId) ?? "{}"
const dataObject = JSON.parse(data)
let GlobalPID = dataObject?.[CurrentPID4Player]?.GlobalPID
if (!GlobalPID)
SetPlayerPID2GlobalPID(playerId, CurrentPID4Player, GlobalPID = GetGlobalPID())
return GlobalPID as number
}

// 每个玩家享受单独的假人限额
const GetPlayerSimulatedPlayerCount = (playerId: string): number => {
const data = <string>world.getDynamicProperty("info4EveryPlayer_" + playerId) ?? "{}"
const dataObject = JSON.parse(data)
let _max_simulatedPlayerCount = dataObject?._max_simulatedPlayerCount
if (!_max_simulatedPlayerCount) {
_max_simulatedPlayerCount = max_simulatedPlayerCount
SetPlayerSimulatedPlayerCount(playerId, _max_simulatedPlayerCount)
world.sendMessage('[模拟玩家] 每个玩家享受单独的假人限额: ' + _max_simulatedPlayerCount)
}
return _max_simulatedPlayerCount as number
}

const SetPlayerSimulatedPlayerCount = (playerId: string, count: number) => {
let data = <string>world.getDynamicProperty("info4EveryPlayer_" + playerId) ?? "{}"
const dataObject = JSON.parse(data)
dataObject._max_simulatedPlayerCount = count
data = JSON.stringify(dataObject)
world.setDynamicProperty("info4EveryPlayer_" + playerId, data)
}



export const initialized: initializedEventSignal = new EventSignal<initializedEvent>()
export const spawned: spawnedEventSignal = new EventSignal<spawnedEvent>()

Expand All @@ -156,8 +85,10 @@ register('我是云梦', '假人', (test: Test) => {
world.gameRules.doDayLightCycle = doDayLightCycle
world.gameRules.doMobSpawning = doMobSpawning

spawnSimulatedPlayer = (location:Vector3, dimension:Dimension, pid: number ):SimulatedPlayer=>{
return spawnSimulatedPlayerByNameTag(location, dimension, `工具人-${pid}`)
}
spawnSimulatedPlayerByNameTag = (location: Vector3, dimension: Dimension, nameTag: string): SimulatedPlayer => {

// 是的,这个nameTag在这里实际先被作为name
const simulatedPlayer = test.spawnSimulatedPlayer({ x: 0, y: 8, z: 0 }, nameTag)
simulatedPlayer.addTag('Backpack2Barrel_init')
Expand Down Expand Up @@ -234,5 +165,5 @@ world.beforeEvents.chatSend.subscribe(({ message, sender }) => {
});
});

export { spawnSimulatedPlayerByNameTag, testWorldLocation, testWorldDimension, GetGlobalPID, GetCurrentPID4Player, GetPlayerPID2GlobalPID }
export { spawnSimulatedPlayerByNameTag, spawnSimulatedPlayer, testWorldLocation, testWorldDimension, currentPID, GetPID }

48 changes: 31 additions & 17 deletions tscripts/xTerrain/plugins/chatSpawn.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,55 @@
import type {SimulatedPlayer} from '@minecraft/server-gametest'

import {
GetCurrentPID4Player,
GetGlobalPID, GetPlayerPID2GlobalPID, initSucceed,
GetPID,
initSucceed,
simulatedPlayers,
spawned as spawnedEvent,
spawned as spawnedEvent, spawnSimulatedPlayer,
spawnSimulatedPlayerByNameTag
} from '../main'
import { type CommandInfo, commandManager, Command } from '../../lib/yumeCommand/CommandRegistry'
import { Dimension, Vector3, world, type Player } from '@minecraft/server'
import {xyz_dododo} from "../../lib/xboyPackage/xyz_dododo";
import { getFakePlayerNameTag } from './Backpack2Barrel';
import {
invalidPlayerNameWarnText, isOutOfLimit,
LIMIT_CONFIG_GLOBAL_CONFIG_KEY, outOfLimitWarnText,
playerSpawnedSimulatedPlayerNumbers
} from "./limitSimplayerNum";

const overworld = world.getDimension("overworld");

const spawnAndRegisterSimulatedPlayer = (entity: Player | undefined, location: Vector3, dimension: Dimension, nameTag?: string): void => {
if (!initSucceed) {
entity?.sendMessage('[假人] 插件未初始化完成,请重试');
entity?.sendMessage?.('[假人] 插件未初始化完成,请重试');
return;
}

const PlayerPID = GetCurrentPID4Player(entity.id);
if (!PlayerPID) {
entity?.sendMessage('[模拟玩家] 假人数量已达上限,请删除假人后再试');
if (entity?.name === LIMIT_CONFIG_GLOBAL_CONFIG_KEY) {
entity.sendMessage(invalidPlayerNameWarnText + '生成');
return;
}
const GlobalPID = GetPlayerPID2GlobalPID(entity.id, PlayerPID);
const name = getFakePlayerNameTag(GlobalPID) ?? nameTag ?? ((entity?.name ?? 'flash') + PlayerPID)
const __FlashPlayer__ = world.scoreboard.getObjective('##FlashPlayer##');
const simulatedPlayer: SimulatedPlayer = spawnSimulatedPlayerByNameTag(location, dimension, name)
if (isOutOfLimit(entity)) {
entity?.sendMessage?.(outOfLimitWarnText);
return;
}

const num = playerSpawnedSimulatedPlayerNumbers[entity.name];
if (!num) {
playerSpawnedSimulatedPlayerNumbers[entity.name] = 1;
} else {
playerSpawnedSimulatedPlayerNumbers[entity.name] = num + 1;
}

const PID = GetPID();
const __FlashPlayer__ = world.scoreboard.getObjective('##FlashPlayer##');
const simulatedPlayer: SimulatedPlayer = nameTag
? spawnSimulatedPlayerByNameTag(location, dimension, nameTag)
: spawnSimulatedPlayer(location, dimension, PID);

simulatedPlayers[GlobalPID] = simulatedPlayer;
simulatedPlayers[simulatedPlayer.id] = GlobalPID;
simulatedPlayers[PID] = simulatedPlayer;
simulatedPlayers[simulatedPlayer.id] = PID;

spawnedEvent.trigger({ spawnedSimulatedPlayer: simulatedPlayer, PID:GlobalPID });
__FlashPlayer__.setScore(simulatedPlayer.id, GlobalPID);
spawnedEvent.trigger({ spawnedSimulatedPlayer: simulatedPlayer, PID });
__FlashPlayer__.setScore(simulatedPlayer.id, PID);
};

const chatSpawnCommand = new Command();
Expand Down
5 changes: 3 additions & 2 deletions tscripts/xTerrain/plugins/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ helpCommand.register(({ args, isEntity }) => args.length === 0 && isEntity, ({ e
"§6▶ 技术说明§r",
" - §e背包会自动保存§r§7,重启游戏不会导致物品丢失。",
" - §7假人可以捡拾地上的物品。",
" - §7使用 §f/scriptevent ffpp:<指令>§7 可在命令方块中执行。",
" - §7使用 §f/scriptevent ffp:<指令>§7 可在命令方块中执行。",
" - §7还可以使用 §f/scriptevent ffpp:<指令>§7。",
" - §7输入 §f假人github§7 可获取项目地址。",
].join('\n');
entity.sendMessage(helpText);
Expand All @@ -106,7 +107,7 @@ helpCommand.register(({ args, isEntity }) => args.length > 0 && isEntity, ({ arg
"创建": ["创建示例", "假人创建", "假人创建 + 空格 + x y z", "假人创建 100 50 0", "假人创建 + 空格 + name", "假人创建 \"fake player\""],
"销毁": ["销毁示例", "假人销毁 + 空格 + 序号", "假人销毁 1", "假人销毁 2"],
"重生": ["重生示例", "假人重生 + 空格 + 序号", "假人重生 1", "假人重生 2"],
"scriptevent": ["scriptevent 示例", "/scriptevent ffpp:ffpp", "/scriptevent ffpp:假人生成 100 50 0", "/scriptevent ffpp:假人销毁 2"]
"scriptevent": ["scriptevent 示例", "/scriptevent ffp:ffpp", "/scriptevent ffpp:假人生成 100 50 0", "/scriptevent ffp:假人销毁 2"]
})
[item]

Expand Down
Loading