Skip to content
KubeJS persistentData Data Storage

持久化数据

概述

persistentData 是 KubeJS 提供的自定义数据存储系统,不是 NBT 数据

适用范围

persistentData 可用于以下对象:

  • 实体(Entity)- 存储实体相关数据
  • 玩家(Player)- 存储玩家个人数据
  • 世界(Level)- 存储世界级别数据
  • 服务器(Server)- 存储全局数据

基础操作

数据类型

方法 数据类型 描述
putString(key, value) 字符串 存储文本数据
getString(key) 字符串 读取文本数据
putInt(key, value) 整数 存储整数
getInt(key) 整数 读取整数
putDouble(key, value) 浮点数 存储小数
getDouble(key) 浮点数 读取小数
putBoolean(key, value) 布尔值 存储真/假值
getBoolean(key) 布尔值 读取真/假值

基础示例

js
// 实体持久化数据存储
entity.persistentData.putString('type', 'boss')
entity.persistentData.putInt('level', 5)
entity.persistentData.putBoolean('defeated', false)

// 数据读取
const type = entity.persistentData.getString('type')
const level = entity.persistentData.getInt('level')
const defeated = entity.persistentData.getBoolean('defeated')

数据检查与管理

检查数据存在

js
// 检查键是否存在
if (player.persistentData.contains('first_join')) {
    // 键存在,执行相应逻辑
} else {
    // 首次使用,设置默认值
    player.persistentData.putBoolean('first_join', true)
}

移除数据

js
// 移除指定键
player.persistentData.remove('temp_data')

// 获取所有键
const allKeys = player.persistentData.getAllKeys()
console.log('存储的键:', allKeys)

复合数据

存储对象数据

js
// 创建复合数据
const playerData = player.persistentData.getCompound('stats')
playerData.putInt('deaths', 0)
playerData.putInt('kills', 0)
playerData.putDouble('playtime', 0.0)

// 保存复合数据
player.persistentData.put('stats', playerData)

读取复合数据

js
// 读取复合数据
const stats = player.persistentData.getCompound('stats')
const deaths = stats.getInt('deaths')
const kills = stats.getInt('kills')
const playtime = stats.getDouble('playtime')

列表数据

NBT 标签类型表

在使用 getList() 方法时需要指定 NBT 标签类型 ID:

ID 标签类型 Java 常量名 描述 示例用法
0 END_TAG TAG_End 结束标签(内部使用) -
1 BYTE_TAG TAG_Byte 字节(-128 到 127) getList('bytes', 1)
2 SHORT_TAG TAG_Short 短整数(-32768 到 32767) getList('shorts', 2)
3 INT_TAG TAG_Int 整数(-2³¹ 到 2³¹-1) getList('scores', 3)
4 LONG_TAG TAG_Long 长整数(-2⁶³ 到 2⁶³-1) getList('timestamps', 4)
5 FLOAT_TAG TAG_Float 单精度浮点数 getList('floats', 5)
6 DOUBLE_TAG TAG_Double 双精度浮点数 getList('coordinates', 6)
7 BYTE_ARRAY_TAG TAG_ByteArray 字节数组 getList('byteArrays', 7)
8 STRING_TAG TAG_String 字符串 getList('friends', 8)
9 LIST_TAG TAG_List 列表(相同类型元素) getList('nested_lists', 9)
10 COMPOUND_TAG TAG_Compound 复合标签(键值对集合) getList('compounds', 10)
11 INT_ARRAY_TAG TAG_IntArray 整数数组 getList('intArrays', 11)
12 LONG_ARRAY_TAG TAG_LongArray 长整数数组 getList('longArrays', 12)

存储列表

js
// 字符串列表
const friends = player.persistentData.getList('friends', 8) // 8 = STRING_TAG
friends.add('player1')
friends.add('player2')

// 整数列表  
const scores = player.persistentData.getList('scores', 3) // 3 = INT_TAG
scores.add(100)
scores.add(250)

// 双精度浮点数列表
const coordinates = player.persistentData.getList('visited_coords', 6) // 6 = DOUBLE_TAG
coordinates.add(100.5)
coordinates.add(64.0)
coordinates.add(200.75)

// 长整数列表(时间戳)
const timestamps = player.persistentData.getList('event_times', 4) // 4 = LONG_TAG
timestamps.add(Date.now())
timestamps.add(1234567890123)

读取列表

js
// 读取字符串列表
const friendsList = player.persistentData.getList('friends', 8) // STRING_TAG
for (let i = 0; i < friendsList.size(); i++) {
    const friendName = friendsList.getString(i)
    console.log('朋友:', friendName)
}

// 读取整数列表
const scoresList = player.persistentData.getList('scores', 3) // INT_TAG
for (let i = 0; i < scoresList.size(); i++) {
    const score = scoresList.getInt(i)
    console.log('分数:', score)
}

// 读取双精度浮点数列表
const coordsList = player.persistentData.getList('visited_coords', 6) // DOUBLE_TAG
for (let i = 0; i < coordsList.size(); i++) {
    const coord = coordsList.getDouble(i)
    console.log('坐标:', coord)
}

// 读取长整数列表
const timestampsList = player.persistentData.getList('event_times', 4) // LONG_TAG
for (let i = 0; i < timestampsList.size(); i++) {
    const timestamp = timestampsList.getLong(i)
    console.log('时间戳:', new Date(timestamp))
}

// 读取字节列表
const bytesList = player.persistentData.getList('flags', 1) // BYTE_TAG
for (let i = 0; i < bytesList.size(); i++) {
    const flag = bytesList.getByte(i)
    console.log('标志:', flag === 1 ? '开启' : '关闭')
}

实际应用示例

玩家数据

js
PlayerEvents.loggedIn(event => {
    const player = event.player
    
    // 检查是否首次加入
    if (!player.persistentData.contains('first_join')) {
        player.persistentData.putBoolean('first_join', true)
        player.persistentData.putLong('join_time', Date.now())
        player.tell('§a欢迎首次加入服务器!')
        
        // 给予新手物品
        player.give('minecraft:bread', 10)
    } else {
        // 更新最后登录时间
        player.persistentData.putLong('last_login', Date.now())
    }
    
    // 增加登录次数
    const loginCount = player.persistentData.getInt('login_count') + 1
    player.persistentData.putInt('login_count', loginCount)
    player.tell(`§7这是你第 ${loginCount} 次登录`)
})

实体

js
EntityEvents.spawned(event => {
    const entity = event.entity
    
    if (entity.type === 'minecraft:zombie') {
        // 随机设置僵尸等级
        const level = Math.floor(Math.random() * 5) + 1
        entity.persistentData.putInt('zombie_level', level)
        
        // 根据等级调整属性
        const health = 20 + (level * 5)
        entity.getAttribute('minecraft:generic.max_health').baseValue = health
        entity.health = health
        
        // 设置名称显示等级
        entity.customName = `§c等级 ${level} 僵尸`
        entity.customNameVisible = true
    }
})

世界数据

js
ServerEvents.tick(event => {
    const level = event.server.overworld()
    
    // 每天更新一次世界数据
    if (event.server.tickCount % 24000 === 0) {
        const currentDay = Math.floor(level.dayTime / 24000)
        level.persistentData.putInt('current_day', currentDay)
        
        // 记录特殊事件
        if (currentDay % 7 === 0) {
            level.persistentData.putBoolean('special_week', true)
            event.server.tell('§6今天是特殊的一周!')
        }
    }
})

标签类型选择指南

常用数据类型推荐

数据类型 推荐标签 标签ID 说明
玩家名称/ID STRING 8 存储文本信息
分数/等级/数量 INT 3 普通整数范围足够
时间戳/大数值 LONG 4 防止数值溢出
坐标/小数值 DOUBLE 6 支持小数精度
开关/标志状态 BYTE 1 0/1 表示布尔值,节省存储空间
复杂数据结构 COMPOUND 10 包含多种数据类型的对象
嵌套列表 LIST 9 存储其他列表的列表

实际应用示例

js
// 玩家好友系统
function addFriend(player, friendName) {
    const friends = player.persistentData.getList('friends', 8) // STRING_TAG
    if (!friends.contains(friendName)) {
        friends.add(friendName)
        player.tell(`§a已添加 ${friendName} 为好友`)
    }
}

// 玩家访问坐标记录
function recordVisitedLocation(player, x, y, z) {
    const locations = player.persistentData.getList('visited_locations', 10) // COMPOUND_TAG
    
    const locationData = {}
    locationData.putDouble('x', x)
    locationData.putDouble('y', y) 
    locationData.putDouble('z', z)
    locationData.putLong('timestamp', Date.now())
    locationData.putString('dimension', player.level.dimension.toString())
    
    locations.add(locationData)
    
    // 限制记录数量
    if (locations.size() > 50) {
        locations.remove(0)
    }
}

// 玩家成就/标志系统
function setPlayerFlag(player, flagName, value) {
    const flags = player.persistentData.getList('achievement_flags', 1) // BYTE_TAG
    const flagNames = player.persistentData.getList('flag_names', 8) // STRING_TAG
    
    let index = -1
    for (let i = 0; i < flagNames.size(); i++) {
        if (flagNames.getString(i) === flagName) {
            index = i
            break
        }
    }
    
    if (index === -1) {
        // 新标志
        flagNames.add(flagName)
        flags.add(value ? 1 : 0)
    } else {
        // 更新现有标志
        flags.set(index, value ? 1 : 0)
    }
}

// 服务器统计数据
function updateServerStats(server) {
    const stats = server.persistentData.getList('daily_stats', 10) // COMPOUND_TAG
    
    const todayStats = {}
    todayStats.putLong('date', Date.now())
    todayStats.putInt('player_count', server.playerList.players.length)
    todayStats.putInt('entity_count', server.overworld().entities.size())
    todayStats.putDouble('tps', server.getAverageTickTime())
    
    stats.add(todayStats)
    
    // 只保留最近30天的数据
    if (stats.size() > 30) {
        stats.remove(0)
    }
}

性能与最佳实践

数据量控制

js
// ✅ 良好实践:限制列表大小
function addToHistory(player, data) {
    const history = player.persistentData.getList('action_history', 8)
    history.add(data)
    
    // 限制历史记录数量,避免数据过大
    const maxSize = 100
    if (history.size() > maxSize) {
        history.remove(0) // 移除最早的记录
    }
}

// ❌ 不良实践:无限制添加数据
function badAddToHistory(player, data) {
    const history = player.persistentData.getList('action_history', 8)
    history.add(data) // 可能导致数据无限增长
}

数据类型选择

js
// ✅ 根据数据范围选择合适的类型
const smallNumbers = player.persistentData.getList('levels', 1)    // BYTE_TAG: -128 到 127
const normalNumbers = player.persistentData.getList('scores', 3)   // INT_TAG: 普通整数
const bigNumbers = player.persistentData.getList('timestamps', 4)  // LONG_TAG: 大整数
const preciseNumbers = player.persistentData.getList('coords', 6)  // DOUBLE_TAG: 小数

// ❌ 过度使用大类型浪费空间
const wasteful = player.persistentData.getList('small_flags', 4)   // LONG_TAG 用于存储 0-10 的值

检查与错误处理

js
// ✅ 安全的列表操作
function safeGetListItem(persistentData, listName, tagType, index) {
    try {
        const list = persistentData.getList(listName, tagType)
        if (index >= 0 && index < list.size()) {
            switch (tagType) {
                case 1: return list.getByte(index)
                case 3: return list.getInt(index) 
                case 4: return list.getLong(index)
                case 6: return list.getDouble(index)
                case 8: return list.getString(index)
                case 10: return list.getCompound(index)
                default: return null
            }
        }
    } catch (error) {
        console.error(`获取列表项失败: ${error}`)
    }
    return null
}