Grasscutter公会活动实现:协作与奖励

Grasscutter公会活动实现:协作与奖励

【免费下载链接】Grasscutter A server software reimplementation for a certain anime game. 【免费下载链接】Grasscutter 项目地址: https://gitcode.com/GitHub_Trending/gr/Grasscutter

引言:团队协作的技术实践

你是否还在为游戏服务器中缺乏团队协作玩法而困扰?作为Grasscutter(一款动漫风格游戏的服务器重构实现)的开发者,公会活动系统的缺失往往导致玩家留存率低下、社区互动不足。本文将从技术角度详解如何基于Grasscutter现有架构实现公会活动系统,涵盖协作机制、奖励分发和数据持久化等核心模块。读完本文,你将能够:

  • 理解Grasscutter多人协作的技术基础
  • 掌握活动任务系统的设计与实现
  • 实现动态奖励机制与数据存储方案
  • 解决公会活动中的并发与同步问题

一、技术架构分析:Grasscutter的协作基因

1.1 现有协作模块解析

Grasscutter虽然未直接提供公会系统,但通过代码分析可发现其已具备多人协作的技术基础。核心协作相关类如下:

// 团队信息类(src/main/java/emu/grasscutter/game/player/TeamInfo.java)
public class TeamInfo {
    private int teamId;
    private List<Integer> avatarIds;
    private int captainPos;
    // 团队管理方法...
}

// 合作请求类(src/main/java/emu/grasscutter/game/CoopRequest.java)
public class CoopRequest {
    private int hostUid;
    private int targetUid;
    private CoopRequestState state;
    private long timestamp;
    // 请求处理方法...
}

1.2 网络通信协议支持

通过协议文件分析发现,Grasscutter已定义多种团队协作相关的通信协议:

// DelBackupAvatarTeamReq.proto
message DelBackupAvatarTeamReq {
    uint32 backup_avatar_team_id = 10;
}

// TakeReunionFirstGiftRewardReq.proto
message TakeReunionFirstGiftRewardReq {
    // 团队奖励领取协议
}

这些协议为公会活动的实现提供了网络层支持,可直接复用或扩展。

1.3 活动系统基础框架

buildSrc/src/main/java/emu/grasscutter/gen/GenerateActivityConditions.java中,发现活动条件生成器,表明Grasscutter已有活动系统的基础架构:

private static final String ACTIVITY_CONDITIONS_SRC = 
    "/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditions.java";

private static final String activityClassStart = """
    package emu.grasscutter.game.activity;
    // 活动系统基础代码...
""";

二、公会活动核心模块实现

2.1 公会数据模型设计

基于现有数据模型,设计公会(Guild)核心数据结构:

public class Guild {
    private String id;                // 公会唯一ID
    private String name;              // 公会名称
    private int level;                // 公会等级
    private List<GuildMember> members; // 公会成员列表
    private GuildActivity activity;   // 当前活动
    private long createdTime;         // 创建时间
    private Map<Integer, Integer> contributions; // 成员贡献度
    
    // 公会管理方法
    public void addMember(Player player) {
        // 添加成员逻辑
    }
    
    public void removeMember(Player player) {
        // 移除成员逻辑
    }
    
    public void startActivity(GuildActivityType type) {
        // 开始公会活动逻辑
    }
}

2.2 协作机制实现

利用Grasscutter现有的Coop系统扩展公会协作机制:

public class GuildCoopManager {
    private final Guild guild;
    private final Map<Integer, CoopInstance> activeCoops;
    
    public CoopInstance createGuildCoop(int activityId, Player host) {
        // 创建公会协作实例
        CoopInstance instance = new CoopInstance(activityId, host);
        
        // 设置协作参数(基于公会规模动态调整)
        instance.setMaxPlayers(guild.getMembers().size());
        instance.setDifficulty(calculateDifficulty(guild.getAverageLevel()));
        
        activeCoops.put(instance.getId(), instance);
        return instance;
    }
    
    public void onPlayerJoinCoop(CoopInstance instance, Player player) {
        // 验证玩家是否为公会成员
        if (!guild.hasMember(player.getUid())) {
            throw new IllegalArgumentException("Player is not in the guild");
        }
        
        // 加入协作逻辑
        instance.addPlayer(player);
        
        // 同步公会活动进度
        guild.getActivity().updateProgress(player, 0);
    }
}

2.3 活动任务系统设计

基于Grasscutter的任务系统,实现公会活动任务:

public class GuildActivity {
    private int id;
    private GuildActivityType type;
    private long startTime;
    private long endTime;
    private Map<Integer, ActivityProgress> memberProgress;
    private GuildActivityReward reward;
    
    // 更新活动进度
    public void updateProgress(Player player, int progress) {
        int uid = player.getUid();
        ActivityProgress p = memberProgress.getOrDefault(uid, new ActivityProgress(uid));
        p.addProgress(progress);
        memberProgress.put(uid, p);
        
        // 检查是否达成公会目标
        checkGuildGoalCompletion();
    }
    
    // 检查公会目标完成情况
    private void checkGuildGoalCompletion() {
        int totalProgress = memberProgress.values().stream()
            .mapToInt(ActivityProgress::getTotal)
            .sum();
            
        if (totalProgress >= getGoal()) {
            completeActivity();
        }
    }
    
    // 完成活动并分发奖励
    private void completeActivity() {
        // 标记活动为完成
        this.setCompleted(true);
        
        // 分发奖励给所有参与成员
        reward.distribute(guild, memberProgress);
    }
}

三、奖励系统实现

3.1 奖励数据结构

设计灵活的公会奖励系统:

public class GuildActivityReward {
    private int baseRewardId;          // 基础奖励ID
    private Map<Integer, Integer> rankRewards; // 排名奖励
    private int contributionRewardId;  // 贡献度奖励ID
    
    // 分发奖励给公会成员
    public void distribute(Guild guild, Map<Integer, ActivityProgress> progress) {
        // 1. 分发基础奖励给所有参与者
        progress.forEach((uid, p) -> {
            if (p.getTotal() > 0) {
                giveReward(guild.getMember(uid), baseRewardId);
            }
        });
        
        // 2. 分发排名奖励
        List<Map.Entry<Integer, ActivityProgress>> sorted = new ArrayList(progress.entrySet());
        sorted.sort((a, b) -> Integer.compare(b.getValue().getTotal(), a.getValue().getTotal()));
        
        for (int i = 0; i < sorted.size() && i < rankRewards.size(); i++) {
            int uid = sorted.get(i).getKey();
            int rewardId = rankRewards.get(i + 1); // 1-based index
            giveReward(guild.getMember(uid), rewardId);
        }
        
        // 3. 分发贡献度奖励
        guild.getMembers().forEach(member -> {
            int contribution = guild.getContribution(member.getUid());
            if (contribution > 0) {
                giveContributionReward(member, contributionRewardId, contribution);
            }
        });
    }
    
    private void giveReward(Player player, int rewardId) {
        // 调用Grasscutter的奖励系统发放奖励
        RewardData reward = GameData.getRewardDataMap().get(rewardId);
        if (reward != null) {
            player.getInventory().addItem(reward);
        }
    }
}

3.2 奖励领取协议实现

利用Grasscutter现有的奖励领取协议:

public class GuildRewardHandler {
    public void handleTakeReunionReward(SceneServer sceneServer, Player player, 
            TakeReunionFirstGiftRewardReq req) {
        // 验证玩家是否有权限领取
        Guild guild = player.getGuild();
        if (guild == null) {
            // 返回错误:玩家不在公会中
            sendErrorResponse(player, ReturnCode.RET_NOT_IN_GUILD);
            return;
        }
        
        GuildActivity activity = guild.getActivity();
        if (activity == null || !activity.isCompleted()) {
            // 返回错误:活动未完成
            sendErrorResponse(player, ReturnCode.RET_ACTIVITY_NOT_COMPLETED);
            return;
        }
        
        // 标记奖励为已领取
        activity.markRewardAsTaken(player.getUid());
        
        // 发送奖励响应
        TakeReunionFirstGiftRewardRsp rsp = TakeReunionFirstGiftRewardRsp.newBuilder()
            .setRetcode(0)
            .build();
            
        player.sendPacket(new PacketTakeReunionFirstGiftRewardRsp(rsp));
    }
}

四、数据持久化方案

4.1 数据库表设计

为公会系统设计数据库表结构:

-- 公会表
CREATE TABLE guilds (
    id VARCHAR(32) PRIMARY KEY,
    name VARCHAR(64) NOT NULL,
    level INT NOT NULL DEFAULT 1,
    created_time BIGINT NOT NULL,
    leader_uid INT NOT NULL,
    activity_id INT,
    activity_start_time BIGINT,
    activity_end_time BIGINT,
    FOREIGN KEY (leader_uid) REFERENCES players(uid)
);

-- 公会成员表
CREATE TABLE guild_members (
    guild_id VARCHAR(32) NOT NULL,
    uid INT NOT NULL,
    role INT NOT NULL, -- 0:普通成员, 1:管理员, 2:会长
    contribution INT NOT NULL DEFAULT 0,
    join_time BIGINT NOT NULL,
    last_active_time BIGINT NOT NULL,
    PRIMARY KEY (guild_id, uid),
    FOREIGN KEY (guild_id) REFERENCES guilds(id),
    FOREIGN KEY (uid) REFERENCES players(uid)
);

-- 公会活动进度表
CREATE TABLE guild_activity_progress (
    guild_id VARCHAR(32) NOT NULL,
    activity_id INT NOT NULL,
    uid INT NOT NULL,
    progress INT NOT NULL DEFAULT 0,
    reward_taken BOOLEAN NOT NULL DEFAULT FALSE,
    PRIMARY KEY (guild_id, activity_id, uid),
    FOREIGN KEY (guild_id) REFERENCES guilds(id),
    FOREIGN KEY (uid) REFERENCES players(uid)
);

4.2 数据访问层实现

实现公会数据的持久化操作:

public class GuildDatabaseManager {
    private final DatabaseHelper dbHelper;
    
    // 保存公会信息
    public void saveGuild(Guild guild) {
        try (Connection connection = dbHelper.getConnection()) {
            // 开始事务
            connection.setAutoCommit(false);
            
            // 保存公会基本信息
            String sql = "INSERT INTO guilds (id, name, level, created_time, leader_uid) VALUES (?, ?, ?, ?, ?) " +
                         "ON DUPLICATE KEY UPDATE name=?, level=?, leader_uid=?";
            
            try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
                pstmt.setString(1, guild.getId());
                pstmt.setString(2, guild.getName());
                pstmt.setInt(3, guild.getLevel());
                pstmt.setLong(4, guild.getCreatedTime());
                pstmt.setInt(5, guild.getLeaderUid());
                
                // 更新部分
                pstmt.setString(6, guild.getName());
                pstmt.setInt(7, guild.getLevel());
                pstmt.setInt(8, guild.getLeaderUid());
                
                pstmt.executeUpdate();
            }
            
            // 保存公会成员
            saveGuildMembers(connection, guild);
            
            // 提交事务
            connection.commit();
        } catch (SQLException e) {
            // 处理异常
            log.error("Failed to save guild", e);
        }
    }
    
    // 保存公会成员
    private void saveGuildMembers(Connection connection, Guild guild) throws SQLException {
        // 删除原有成员
        try (PreparedStatement pstmt = connection.prepareStatement(
            "DELETE FROM guild_members WHERE guild_id = ?")) {
            pstmt.setString(1, guild.getId());
            pstmt.executeUpdate();
        }
        
        // 插入新成员列表
        String sql = "INSERT INTO guild_members (guild_id, uid, role, contribution, join_time, last_active_time) " +
                     "VALUES (?, ?, ?, ?, ?, ?)";
        
        try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
            for (GuildMember member : guild.getMembers()) {
                pstmt.setString(1, guild.getId());
                pstmt.setInt(2, member.getUid());
                pstmt.setInt(3, member.getRole().ordinal());
                pstmt.setInt(4, member.getContribution());
                pstmt.setLong(5, member.getJoinTime());
                pstmt.setLong(6, member.getLastActiveTime());
                
                pstmt.addBatch();
            }
            
            pstmt.executeBatch();
        }
    }
}

五、并发控制与同步

5.1 分布式锁实现

使用Redis实现分布式锁,确保公会活动数据的一致性:

public class RedisLock {
    private final JedisPool jedisPool;
    private final String lockKey;
    private final int expireSeconds;
    private String lockValue;
    
    public RedisLock(JedisPool jedisPool, String lockKey, int expireSeconds) {
        this.jedisPool = jedisPool;
        this.lockKey = lockKey;
        this.expireSeconds = expireSeconds;
    }
    
    public boolean acquire() {
        try (Jedis jedis = jedisPool.getResource()) {
            lockValue = UUID.randomUUID().toString();
            String result = jedis.set(lockKey, lockValue, "NX", "EX", expireSeconds);
            return "OK".equals(result);
        }
    }
    
    public boolean release() {
        try (Jedis jedis = jedisPool.getResource()) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Long result = (Long) jedis.eval(script, Collections.singletonList(lockKey), 
                                           Collections.singletonList(lockValue));
            return result != null && result > 0;
        }
    }
}

5.2 活动进度同步

实现公会活动进度的并发更新:

public class GuildActivityProgressManager {
    private final RedisLockFactory lockFactory;
    
    public void updateProgress(Guild guild, Player player, int progress) {
        String lockKey = "guild:activity:lock:" + guild.getId();
        RedisLock lock = lockFactory.createLock(lockKey, 5); // 5秒过期
        
        try {
            if (lock.acquire()) {
                // 获取公会活动数据(加锁后确保数据一致性)
                GuildActivity activity = guild.getActivity();
                
                // 更新进度
                activity.updateProgress(player, progress);
                
                // 保存更新后的数据
                guildDatabaseManager.saveActivityProgress(guild.getId(), activity);
            } else {
                // 获取锁失败,重试或返回错误
                throw new ConcurrentModificationException("Failed to acquire lock for guild activity");
            }
        } finally {
            lock.release();
        }
    }
}

六、完整实现流程图

mermaid

七、性能优化建议

7.1 数据库优化

  • 为公会成员表和活动进度表添加适当索引
  • 使用数据库连接池管理连接
  • 对热门公会活动数据进行缓存
public class GuildCacheManager {
    private final LoadingCache<String, Guild> guildCache;
    
    public GuildCacheManager() {
        guildCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(30, TimeUnit.MINUTES)
            .build(new CacheLoader<String, Guild>() {
                @Override
                public Guild load(String guildId) throws Exception {
                    return guildDatabaseManager.getGuildById(guildId);
                }
            });
    }
    
    public Guild getGuild(String guildId) {
        try {
            return guildCache.get(guildId);
        } catch (ExecutionException e) {
            log.error("Failed to get guild from cache", e);
            return null;
        }
    }
    
    public void invalidateGuildCache(String guildId) {
        guildCache.invalidate(guildId);
    }
}

7.2 网络优化

  • 批量处理活动进度更新请求
  • 减少不必要的广播消息
  • 对大型公会活动使用分片处理

八、总结与展望

本文详细介绍了基于Grasscutter实现公会活动系统的完整方案,从数据模型设计到协作机制实现,再到奖励系统和数据持久化,覆盖了公会活动的各个方面。通过复用Grasscutter现有的协作和活动基础架构,可以显著降低实现复杂度。

未来可以进一步扩展以下功能:

  1. 公会技能系统:为公会添加独特技能,增强团队协作效果
  2. 跨服公会战:实现服务器间的公会竞争
  3. 公会商店:基于贡献度的专属商店系统
  4. 公会建筑:可升级的公会基地,提供各种功能加成

通过这些扩展,可以进一步丰富公会玩法,增强玩家的社区归属感和游戏粘性。

九、参考代码

完整实现可参考以下文件结构组织代码:

src/main/java/emu/grasscutter/game/guild/
├── Guild.java
├── GuildMember.java
├── GuildActivity.java
├── GuildCoopManager.java
├── GuildReward.java
├── GuildDatabaseManager.java
├── GuildActivityProgress.java
└── GuildCacheManager.java

通过以上实现,即可为Grasscutter添加完整的公会活动系统,为玩家提供丰富的协作玩法和奖励机制。


如果觉得本文对你有帮助,请点赞、收藏、关注三连,后续将带来更多Grasscutter高级开发教程!

【免费下载链接】Grasscutter A server software reimplementation for a certain anime game. 【免费下载链接】Grasscutter 项目地址: https://gitcode.com/GitHub_Trending/gr/Grasscutter

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值