小熊1986

导航

第六天 单线程的设计

单线程的设计

一.增加攻击的类UserAttkCmdHandler继承ICmHandler类

思路:攻击者攻击以后被攻击者掉血,现在这个掉血是写死的,已经在程序中有相应的对照,我们需要攻击者的id,被攻击者的id,以及调用的量的计算(ps这里可以自己diy)

1.首先给User对象增加一个当前血量的属性

package org.model;

/**
 * @author joy
 * @version 1.0
 * @date 2020/4/28 22:28
 * 用户操作
 */
public class User {
    //用户id
    public int userId;

    //用户形象
    public String heroAvatar;

    //移动状态
    public final MoveState moveState = new MoveState();
    //血量
    public int currHp;
}

2.根据GameMsgProtocol结构攻击的消息机构我们重新写一个类UserAttkCmdHandler来进行解码逻辑操作。

package org.cmdHandler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import org.joy.game.Broadcaster;
import org.model.User;
import org.model.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinygame.herostory.msg.GameMsgProtocol;

/**
 * @author joy
 * @version 1.0
 * @date 2020/5/9 17:46
 */
public class UserAttkCmdHandler implements ICmdHandler<GameMsgProtocol.UserAttkCmd> {
    private static Logger LOGGER = LoggerFactory.getLogger(UserAttkCmdHandler.class);

    @Override
    public void handle(ChannelHandlerContext ctx, GameMsgProtocol.UserAttkCmd cmd) {
        //判断数据不能为空
        if (null == ctx || null == cmd) {
            return;
        }
        //获取攻击者id
        Integer attkUserId = (Integer) ctx.channel().attr(AttributeKey.valueOf("userId")).get();
        if (null == attkUserId) {
            LOGGER.info("攻击者没有");
            return;
        }

        //攻击者
        int targetUserId = cmd.getTargetUserId();

        //创建攻击消息体
        GameMsgProtocol.UserAttkResult.Builder result = GameMsgProtocol.UserAttkResult.newBuilder();
        result.setAttkUserId(attkUserId);
        result.setTargetUserId(targetUserId);
        GameMsgProtocol.UserAttkResult newResult = result.build();
        Broadcaster.sendAllMsg(newResult);

        //受到攻击者
        User targetUser = UserManager.getUserById(targetUserId);
        if (null == targetUser) {
            return;
        }

        //减血量
        int subtractHp = 10;
        targetUser.currHp = targetUser.currHp - subtractHp;

        //广播减血操作
        broadcastSubtractHp(targetUserId, subtractHp);

        //如果是血量被攻击的人见血
        if (targetUser.currHp <= 0) {
            //死亡消息
            broadcastDie(targetUserId);
        }
    }

    /**
     * 死亡消息
     *
     * @param targetId 谁死亡了
     */
    static private void broadcastDie(int targetId) {
        if (targetId < 0) {
            LOGGER.error("死亡消息的id出错id={}", targetId);
            return;
        }

        //创建对象
        GameMsgProtocol.UserDieResult.Builder result = GameMsgProtocol.UserDieResult.newBuilder();
        result.setTargetUserId(targetId);

        //装入到对应的数据里面
        GameMsgProtocol.UserDieResult newResult = result.build();
        Broadcaster.sendAllMsg(newResult);
    }

    /**
     * 见血的消息
     *
     * @param targetUserId 受攻击者的id
     * @param subtractHp   攻击的血量
     */
    static private void broadcastSubtractHp(int targetUserId, int subtractHp) {
        if (targetUserId < 0 || subtractHp < 0) {
            return;
        }

        //创建返回对象
        GameMsgProtocol.UserSubtractHpResult.Builder result = GameMsgProtocol.UserSubtractHpResult.newBuilder();
        result.setTargetUserId(targetUserId);
        result.setSubtractHp(subtractHp);

        GameMsgProtocol.UserSubtractHpResult newResult = result.build();
        Broadcaster.sendAllMsg(newResult);
    }
}

二.出现多线程变量不同步的问题

解决方案:使用单线程进行操作最简单还不容易出错

1.我们创建一个MainThreadProcessor的自己的单线程的类

111

MainThreadprocessor是一个单例的类

核心就是创建一个单线程

/**
 * 创建一个单线程
 */
private final ExecutorService _es = Executors.newSingleThreadExecutor(
        (newRunnable) -> {
            Thread newThread = new Thread(newRunnable);
            newThread.setName("MainThreadProcessor");//设置线程名字
            return newThread;
        });

核心在如下代码

public void process(ChannelHandlerContext ctx, GeneratedMessageV3 msg) {
        if (null == ctx || null == msg) {
            return;
        }
        //获取消息类
        Class<?> msgClazz = msg.getClass();

        //线程处理
        _es.submit(() -> {
            LOGGER.info(
                    "收到客户端消息, msgClazz = {}, msg = {}",
                    msgClazz.getName(),
                    msg
            );

            ICmdHandler<? extends GeneratedMessageV3>
                    cmdHandler = CmdHandlerFactory.crate(msgClazz);
            if (null != cmdHandler) {
                cmdHandler.handle(ctx, cast(msg));
            } else {
                LOGGER.info("未找到对应的指令器,msgClazz={}", msgClazz.getName());
                return;
            }
        });
    }

这样做以后程序就不会出现多线程对属性进行读取的时候的错误。

git地址

posted on 2020-11-23 12:24  小熊1986  阅读(50)  评论(0编辑  收藏  举报