Files
Sprout-Farm/Server/Feature_RemoveCrop.md

8.8 KiB
Raw Blame History

铲除作物功能实现

功能概述

实现了玩家可以花费500金钱铲除地块上的作物将地块变成空地的功能。这个功能完全基于服务器端处理确保数据的一致性和安全性。

功能特点

  • 费用固定铲除任何作物都需要花费500金钱
  • 服务器验证:所有验证和处理都在服务器端完成
  • 状态重置:铲除后地块变成空地,可以重新种植
  • 访客保护:访客模式下无法进行铲除操作
  • 实时更新:操作完成后立即更新客户端显示

实现架构

1. 服务器端实现

消息路由

elif message_type == "remove_crop":
    return self._handle_remove_crop(client_id, message)

主处理方法

def _handle_remove_crop(self, client_id, message):
    """处理铲除作物请求"""
    # 检查用户是否已登录
    logged_in, response = self._check_user_logged_in(client_id, "铲除作物", "remove_crop")
    if not logged_in:
        return self.send_data(client_id, response)
    
    # 获取玩家数据
    player_data, username, response = self._load_player_data_with_check(client_id, "remove_crop")
    if not player_data:
        return self.send_data(client_id, response)
    
    lot_index = message.get("lot_index", -1)
    
    # 验证地块索引
    if lot_index < 0 or lot_index >= len(player_data.get("farm_lots", [])):
        return self._send_action_error(client_id, "remove_crop", "无效的地块索引")
    
    lot = player_data["farm_lots"][lot_index]
    
    # 检查地块状态
    if not lot.get("is_planted", False) or not lot.get("crop_type", ""):
        return self._send_action_error(client_id, "remove_crop", "此地块没有种植作物")
    
    # 处理铲除
    return self._process_crop_removal(client_id, player_data, username, lot, lot_index)

铲除处理逻辑

def _process_crop_removal(self, client_id, player_data, username, lot, lot_index):
    """处理铲除作物逻辑"""
    # 铲除费用
    removal_cost = 500
    
    # 检查玩家金钱是否足够
    if player_data["money"] < removal_cost:
        return self._send_action_error(client_id, "remove_crop", f"金钱不足,铲除作物需要 {removal_cost} 金钱")
    
    # 获取作物名称用于日志
    crop_type = lot.get("crop_type", "未知作物")
    
    # 执行铲除操作
    player_data["money"] -= removal_cost
    lot["is_planted"] = False
    lot["crop_type"] = ""
    lot["grow_time"] = 0
    lot["is_dead"] = False  # 重置死亡状态
    
    # 保存玩家数据
    self.save_player_data(username, player_data)
    
    # 发送作物更新
    self._push_crop_update_to_player(username, player_data)
    
    self.log('INFO', f"玩家 {username} 铲除了地块 {lot_index} 的作物 {crop_type},花费 {removal_cost} 金钱", 'SERVER')
    
    return self.send_data(client_id, {
        "type": "action_response",
        "action_type": "remove_crop",
        "success": True,
        "message": f"成功铲除作物 {crop_type},花费 {removal_cost} 金钱",
        "updated_data": {
            "money": player_data["money"],
            "farm_lots": player_data["farm_lots"]
        }
    })

2. 网络通信

客户端发送请求

#发送铲除作物信息
func sendRemoveCrop(lot_index):
    if not client.is_client_connected():
        return false
        
    client.send_data({
        "type": "remove_crop",
        "lot_index": lot_index,
        "timestamp": Time.get_unix_time_from_system()
    })
    return true

3. 客户端UI实现

按钮文本更新

# 更新按钮文本
func _update_button_texts():
    dig_button.text = "开垦"+"\n花费:"+str(main_game.dig_money)
    remove_button.text = "铲除"+"\n花费500"

铲除操作处理

#铲除
func _on_remove_button_pressed():
    # 检查是否处于访问模式
    if main_game.is_visiting_mode:
        Toast.show("访问模式下无法铲除作物", Color.ORANGE, 2.0, 1.0)
        self.hide()
        return
    
    # 检查玩家金钱是否足够
    var removal_cost = 500
    if main_game.money < removal_cost:
        Toast.show("金钱不足,铲除作物需要 " + str(removal_cost) + " 金钱", Color.RED, 2.0, 1.0)
        self.hide()
        return
    
    # 检查地块是否有作物
    var lot = main_game.farm_lots[selected_lot_index]
    if not lot.get("is_planted", false) or lot.get("crop_type", "") == "":
        Toast.show("此地块没有种植作物", Color.ORANGE, 2.0, 1.0)
        self.hide()
        return
    
    # 发送铲除作物请求到服务器
    if network_manager and network_manager.is_connected_to_server():
        if network_manager.sendRemoveCrop(selected_lot_index):
            Toast.show("正在铲除作物...", Color.YELLOW, 1.5, 1.0)
            self.hide()
        else:
            Toast.show("发送铲除请求失败", Color.RED, 2.0, 1.0)
            self.hide()
    else:
        Toast.show("网络未连接,无法铲除作物", Color.RED, 2.0, 1.0)
        self.hide()

验证机制

服务器端验证

  1. 用户登录验证:确保用户已登录
  2. 地块索引验证:检查地块索引是否有效
  3. 地块状态验证:确保地块有作物可以铲除
  4. 金钱验证:检查玩家金钱是否足够支付铲除费用

客户端预验证

  1. 访问模式检查:访客模式下禁止操作
  2. 金钱预检查:提前检查金钱是否足够
  3. 地块状态预检查:确保地块有作物
  4. 网络连接检查:确保能够发送请求

操作流程

正常流程

  1. 玩家点击地块,显示操作面板
  2. 面板显示铲除按钮和费用信息
  3. 玩家点击铲除按钮
  4. 客户端进行预验证
  5. 发送铲除请求到服务器
  6. 服务器验证并处理请求
  7. 服务器返回操作结果
  8. 客户端更新UI显示

错误处理

  • 金钱不足:显示错误提示,不发送请求
  • 无作物:显示提示信息,不发送请求
  • 访客模式:显示权限提示,不发送请求
  • 网络错误:显示网络错误提示
  • 服务器错误:显示服务器返回的错误信息

数据更新

地块状态重置

lot["is_planted"] = False      # 取消种植状态
lot["crop_type"] = ""          # 清空作物类型
lot["grow_time"] = 0           # 重置生长时间
lot["is_dead"] = False         # 重置死亡状态

玩家数据更新

player_data["money"] -= removal_cost  # 扣除金钱

实时同步

  • 服务器保存玩家数据到文件
  • 推送作物更新到客户端
  • 客户端接收并更新UI显示

使用场景

  1. 清理死亡作物:当作物死亡时,玩家可以花费金钱清理
  2. 重新规划农场:玩家想要种植不同作物时
  3. 紧急处理:当玩家需要快速清理地块时
  4. 策略调整:根据市场需求调整种植策略

安全考虑

  1. 服务器权威:所有验证和处理都在服务器端
  2. 数据一致性:确保客户端和服务器数据同步
  3. 防作弊:客户端无法直接修改游戏数据
  4. 访问控制:访客模式下无法进行破坏性操作

扩展性

该功能设计具有良好的扩展性:

  1. 费用可配置:可以根据作物类型设置不同的铲除费用
  2. 条件扩展:可以添加更多的铲除条件(如等级要求)
  3. 奖励机制:可以在铲除时给予部分资源回收
  4. 工具系统:可以引入铲子等工具来影响铲除效果

测试用例

测试用例1正常铲除

  1. 玩家有足够金钱≥500
  2. 地块有作物
  3. 点击铲除按钮
  4. 验证金钱减少500地块变成空地

测试用例2金钱不足

  1. 玩家金钱不足(<500
  2. 地块有作物
  3. 点击铲除按钮
  4. 验证:显示金钱不足提示,操作失败

测试用例3无作物地块

  1. 玩家有足够金钱
  2. 地块为空地
  3. 点击铲除按钮
  4. 验证:显示无作物提示,操作失败

测试用例4访客模式

  1. 玩家处于访客模式
  2. 点击铲除按钮
  3. 验证:显示权限提示,操作失败

测试用例5网络断开

  1. 网络连接断开
  2. 点击铲除按钮
  3. 验证:显示网络错误提示,操作失败

总结

铲除作物功能的实现遵循了以下设计原则:

  • 服务器权威:所有关键逻辑在服务器端处理
  • 用户体验:提供清晰的费用信息和操作反馈
  • 数据安全:多层验证确保数据完整性
  • 错误处理:完善的错误提示和处理机制
  • 代码复用:利用现有的验证和处理框架
  • 可维护性:清晰的代码结构和文档

这个功能为玩家提供了更灵活的农场管理选项,同时保持了游戏的平衡性和数据安全性。