限时头像框
parent
6c0c612fae
commit
664b4c748c
|
@ -308,4 +308,6 @@ public interface BIReason {
|
||||||
int WORLD_ARENA = 1077;//跨服竞技场
|
int WORLD_ARENA = 1077;//跨服竞技场
|
||||||
|
|
||||||
int SHEJI_ACTIVITY_INIT_CONSUME = 1078;//社稷大典开始清除往期道具
|
int SHEJI_ACTIVITY_INIT_CONSUME = 1078;//社稷大典开始清除往期道具
|
||||||
|
|
||||||
|
int USE_HEADFRAME = 1088;// 使用头像框
|
||||||
}
|
}
|
|
@ -170,7 +170,8 @@ public class GetPlayerInfoHandler extends BaseHandler{
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理玩家旧头像框,兼容,一个玩家只执行一次
|
||||||
|
PlayerLogic.getInstance().checkOldHeadFrame(user);
|
||||||
|
|
||||||
//hotfix 月卡
|
//hotfix 月卡
|
||||||
PlayerLogic.getInstance().getMonthCardInfo(user);
|
PlayerLogic.getInstance().getMonthCardInfo(user);
|
||||||
|
@ -184,8 +185,8 @@ public class GetPlayerInfoHandler extends BaseHandler{
|
||||||
Poster.getPoster().dispatchEvent(new NewWelfareEvent(user, NewWelfareTypeEnum.RECHARGE_BUY.getType(),Global.LMONTHCARDID));
|
Poster.getPoster().dispatchEvent(new NewWelfareEvent(user, NewWelfareTypeEnum.RECHARGE_BUY.getType(),Global.LMONTHCARDID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//重载功能商店
|
||||||
StoreLogic.reloadStoreInfo(user,7);//重载功能商店
|
StoreLogic.reloadStoreInfo(user,7);
|
||||||
PlayerInfoProto.GetPlayerInfoResponse getPlayerInfoResponse
|
PlayerInfoProto.GetPlayerInfoResponse getPlayerInfoResponse
|
||||||
= PlayerInfoProto.GetPlayerInfoResponse.newBuilder()
|
= PlayerInfoProto.GetPlayerInfoResponse.newBuilder()
|
||||||
.setPlayer(player)
|
.setPlayer(player)
|
||||||
|
@ -221,6 +222,7 @@ public class GetPlayerInfoHandler extends BaseHandler{
|
||||||
.setHadAdvanceLuckTime(user.getPlayerInfoManager().getLuckAdvanceWheelLimitTime())
|
.setHadAdvanceLuckTime(user.getPlayerInfoManager().getLuckAdvanceWheelLimitTime())
|
||||||
.setWorldLeve(GlobleSystemLogic.getGloableWorldLeveCache())
|
.setWorldLeve(GlobleSystemLogic.getGloableWorldLeveCache())
|
||||||
.addAllDemonsTrialRewardInfo(user.getMapManager().getDemonsTrialRewardInfo())
|
.addAllDemonsTrialRewardInfo(user.getMapManager().getDemonsTrialRewardInfo())
|
||||||
|
.addAllHeadFrameBag(CBean2Proto.getHeadFrameList(user))
|
||||||
.build();
|
.build();
|
||||||
ReportUtil.onReportEvent(user, ReportEventEnum.APP_LOGIN.getType());
|
ReportUtil.onReportEvent(user, ReportEventEnum.APP_LOGIN.getType());
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package com.ljsd.jieling.handler.player;
|
||||||
|
|
||||||
|
import com.google.protobuf.GeneratedMessage;
|
||||||
|
import com.ljsd.jieling.exception.ErrorCode;
|
||||||
|
import com.ljsd.jieling.exception.ErrorCodeException;
|
||||||
|
import com.ljsd.jieling.globals.BIReason;
|
||||||
|
import com.ljsd.jieling.handler.BaseHandler;
|
||||||
|
import com.ljsd.jieling.logic.dao.UserManager;
|
||||||
|
import com.ljsd.jieling.logic.dao.root.User;
|
||||||
|
import com.ljsd.jieling.logic.item.ItemLogic;
|
||||||
|
import com.ljsd.jieling.util.ItemUtil;
|
||||||
|
import config.SItem;
|
||||||
|
import config.SPlayerHeadIcon;
|
||||||
|
import manager.STableManager;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import rpc.protocols.MessageTypeProto;
|
||||||
|
import rpc.protocols.PlayerInfoProto;
|
||||||
|
import util.TimeUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hj
|
||||||
|
* 使用头像框
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class UseHeadFrameHandler extends BaseHandler<PlayerInfoProto.UseHeadFrameRequest> {
|
||||||
|
@Override
|
||||||
|
public MessageTypeProto.MessageType getMessageCode() {
|
||||||
|
return MessageTypeProto.MessageType.UseHeadFrameRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeneratedMessage processWithProto(int uid, PlayerInfoProto.UseHeadFrameRequest proto) throws Exception {
|
||||||
|
// 用户信息
|
||||||
|
User user = UserManager.getUser(uid);
|
||||||
|
if (user == null) {
|
||||||
|
throw new ErrorCodeException(ErrorCode.UNKNOWN);
|
||||||
|
}
|
||||||
|
// 头像框id
|
||||||
|
int headFrameId = proto.getHeadFrameId();
|
||||||
|
// 读配置表
|
||||||
|
SPlayerHeadIcon headIcon = SPlayerHeadIcon.getHeadIconMapByType().get(SPlayerHeadIcon.HeadFrameType).get(headFrameId);
|
||||||
|
if (headIcon == null){
|
||||||
|
throw new ErrorCodeException(ErrorCode.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证道具是否足够
|
||||||
|
SItem sItem = SItem.getsItemMap().get(headFrameId);
|
||||||
|
boolean result = ItemUtil.checkCost(user, sItem, 1, BIReason.USE_HEADFRAME,headFrameId);
|
||||||
|
if (!result){
|
||||||
|
throw new ErrorCodeException(ErrorCode.ITEM_NOT_ENOUGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否已经使用
|
||||||
|
Long time = user.getPlayerInfoManager().getHeadFrameValidTime().get(headFrameId);
|
||||||
|
if (time != null){
|
||||||
|
throw new ErrorCodeException(ErrorCode.newDefineCode("头像框使用中,不能再次使用"));
|
||||||
|
}
|
||||||
|
// 赋值到期时间
|
||||||
|
long now = TimeUtils.now() / 1000;
|
||||||
|
long endTime = now + headIcon.getTime();
|
||||||
|
user.getPlayerInfoManager().putHeadFrameValidTime(headFrameId,endTime);
|
||||||
|
|
||||||
|
return PlayerInfoProto.UseHeadFrameResponse.newBuilder().setValidTime((int)endTime).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,12 +2,16 @@ package com.ljsd.jieling.logic.dao;
|
||||||
|
|
||||||
|
|
||||||
import com.ljsd.common.mogodb.MongoBase;
|
import com.ljsd.common.mogodb.MongoBase;
|
||||||
|
import com.ljsd.jieling.config.clazzStaticCfg.CommonStaticConfig;
|
||||||
import com.ljsd.jieling.core.VipPrivilegeType;
|
import com.ljsd.jieling.core.VipPrivilegeType;
|
||||||
import com.ljsd.jieling.globals.Global;
|
import com.ljsd.jieling.globals.Global;
|
||||||
import com.ljsd.jieling.logic.dao.root.User;
|
import com.ljsd.jieling.logic.dao.root.User;
|
||||||
|
import com.ljsd.jieling.util.MessageUtil;
|
||||||
import config.SMonthcardConfig;
|
import config.SMonthcardConfig;
|
||||||
import config.SPrivilegeTypeConfig;
|
import config.SPrivilegeTypeConfig;
|
||||||
import manager.STableManager;
|
import manager.STableManager;
|
||||||
|
import rpc.protocols.MessageTypeProto;
|
||||||
|
import rpc.protocols.PlayerInfoProto;
|
||||||
import util.MathUtils;
|
import util.MathUtils;
|
||||||
import util.TimeUtils;
|
import util.TimeUtils;
|
||||||
|
|
||||||
|
@ -80,6 +84,12 @@ public class PlayerManager extends MongoBase {
|
||||||
|
|
||||||
private int headFrame;
|
private int headFrame;
|
||||||
|
|
||||||
|
// 头像框id,有效时间
|
||||||
|
private Map<Integer,Long> headFrameValidTime;
|
||||||
|
|
||||||
|
// 限时头像旧版本兼容
|
||||||
|
private int oldHeadFrame;
|
||||||
|
|
||||||
private int treasureLevel; //孙龙的宝藏等级
|
private int treasureLevel; //孙龙的宝藏等级
|
||||||
|
|
||||||
private int hadBuyTreasure; //是否已购
|
private int hadBuyTreasure; //是否已购
|
||||||
|
@ -476,6 +486,20 @@ public class PlayerManager extends MongoBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHeadFrame() {
|
public int getHeadFrame() {
|
||||||
|
// 获取头像框有效时间
|
||||||
|
Long time = headFrameValidTime.getOrDefault(headFrame,0L);
|
||||||
|
// 当前时间,秒
|
||||||
|
long now = TimeUtils.now() / 1000;
|
||||||
|
// 默认头像框id
|
||||||
|
int headFrameId = STableManager.getFigureConfig(CommonStaticConfig.class).getGameSetting().getDefaultPicture();
|
||||||
|
// 头像框不为永久且时间到期
|
||||||
|
if (time != 0 && now > time){
|
||||||
|
// 赋值默认
|
||||||
|
setHeadFrame(headFrameId);
|
||||||
|
// 删除
|
||||||
|
removeHeadFrameValidTime(headFrameId);
|
||||||
|
return headFrameId;
|
||||||
|
}
|
||||||
return headFrame;
|
return headFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,6 +508,39 @@ public class PlayerManager extends MongoBase {
|
||||||
this.headFrame = headFrame;
|
this.headFrame = headFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<Integer, Long> getHeadFrameValidTime() {
|
||||||
|
// 为空,初始化
|
||||||
|
if (headFrameValidTime == null){
|
||||||
|
setHeadFrameValidTime(new HashMap<>());
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return headFrameValidTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeadFrameValidTime(Map<Integer, Long> headFrameValidTime) {
|
||||||
|
this.headFrameValidTime = headFrameValidTime;
|
||||||
|
updateString("headFrameValidTime", headFrameValidTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putHeadFrameValidTime(int headFrameId,Long time) {
|
||||||
|
getHeadFrameValidTime();
|
||||||
|
headFrameValidTime.put(headFrameId,time);
|
||||||
|
updateString("headFrameValidTime", headFrameValidTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeHeadFrameValidTime(int headFrameId) {
|
||||||
|
getHeadFrameValidTime();
|
||||||
|
headFrameValidTime.remove(headFrameId);
|
||||||
|
updateString("headFrameValidTime", headFrameValidTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOldHeadFrame() {
|
||||||
|
return oldHeadFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOldHeadFrame(int oldHeadFrame) {
|
||||||
|
this.oldHeadFrame = oldHeadFrame;
|
||||||
|
}
|
||||||
|
|
||||||
public int getGuildId() {
|
public int getGuildId() {
|
||||||
return guildId;
|
return guildId;
|
||||||
|
|
|
@ -123,7 +123,9 @@ public class UserManager {
|
||||||
playerManager.setIp(ip);
|
playerManager.setIp(ip);
|
||||||
playerManager.setChannel_id(channle_id);
|
playerManager.setChannel_id(channle_id);
|
||||||
playerManager.setBundle_id(bundle_id);
|
playerManager.setBundle_id(bundle_id);
|
||||||
playerManager.setHeadFrame(STableManager.getFigureConfig(CommonStaticConfig.class).getGameSetting().getDefaultPicture());
|
int headFrameId = STableManager.getFigureConfig(CommonStaticConfig.class).getGameSetting().getDefaultPicture();
|
||||||
|
playerManager.setHeadFrame(headFrameId);
|
||||||
|
playerManager.putHeadFrameValidTime(headFrameId,0L);
|
||||||
SPlayerLevelConfig sPlayerLevelConfig = SPlayerLevelConfig.getsPlayerLevelConfigMap().get(1);
|
SPlayerLevelConfig sPlayerLevelConfig = SPlayerLevelConfig.getsPlayerLevelConfigMap().get(1);
|
||||||
playerManager.setMaxStamina(sPlayerLevelConfig.getMaxEnergy());
|
playerManager.setMaxStamina(sPlayerLevelConfig.getMaxEnergy());
|
||||||
playerManager.setRideLevel(1);
|
playerManager.setRideLevel(1);
|
||||||
|
|
|
@ -440,8 +440,18 @@ public class PlayerLogic {
|
||||||
throw new ErrorCodeException(ErrorCode.newDefineCode("无此物品"));
|
throw new ErrorCodeException(ErrorCode.newDefineCode("无此物品"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 当前时间,秒
|
||||||
|
long now = TimeUtils.now()/1000;
|
||||||
switch (type){
|
switch (type){
|
||||||
case 0:
|
case 0:
|
||||||
|
// 获取头像框有效时间
|
||||||
|
Long time = user.getPlayerInfoManager().getHeadFrameValidTime().get(decorationId);
|
||||||
|
if (time == null){
|
||||||
|
throw new ErrorCodeException(ErrorCode.newDefineCode("无此头像框"));
|
||||||
|
}
|
||||||
|
if (time != 0 && now > time){
|
||||||
|
throw new ErrorCodeException(ErrorCode.newDefineCode("头像框时间到期,更换无效"));
|
||||||
|
}
|
||||||
user.getPlayerInfoManager().setHeadFrame(decorationId);
|
user.getPlayerInfoManager().setHeadFrame(decorationId);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -456,16 +466,10 @@ public class PlayerLogic {
|
||||||
case 4:
|
case 4:
|
||||||
user.getPlayerInfoManager().setDecoration(decorationId);
|
user.getPlayerInfoManager().setDecoration(decorationId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(type==0){
|
|
||||||
user.getPlayerInfoManager().setHeadFrame(decorationId);
|
|
||||||
}else if (type ==1){
|
|
||||||
user.getPlayerInfoManager().setHead(decorationId);
|
|
||||||
}else if(type==2){
|
|
||||||
|
|
||||||
}
|
|
||||||
playerInfoUpdate(user);
|
playerInfoUpdate(user);
|
||||||
MessageUtil.sendMessage(session,1,messageType.getNumber(),null,true);
|
MessageUtil.sendMessage(session,1,messageType.getNumber(),null,true);
|
||||||
}
|
}
|
||||||
|
@ -971,4 +975,45 @@ public class PlayerLogic {
|
||||||
});
|
});
|
||||||
return cardInfos;
|
return cardInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限时头像框,旧内容兼容,一个玩家只运行一次
|
||||||
|
* @param user
|
||||||
|
*/
|
||||||
|
public void checkOldHeadFrame(User user){
|
||||||
|
// 已经处理过,不再执行
|
||||||
|
int oldHeadFrame = user.getPlayerInfoManager().getOldHeadFrame();
|
||||||
|
if (oldHeadFrame == 1){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取玩家道具新
|
||||||
|
ItemManager itemManager = user.getItemManager();
|
||||||
|
// 头像框到期时间
|
||||||
|
Map<Integer, Long> validTime = user.getPlayerInfoManager().getHeadFrameValidTime();
|
||||||
|
// 获取头像框表
|
||||||
|
Map<Integer, SPlayerHeadIcon> headIconMap = SPlayerHeadIcon.getHeadIconMapByType().get(SPlayerHeadIcon.HeadFrameType);
|
||||||
|
// 当前时间,秒
|
||||||
|
long now = TimeUtils.now() / 1000;
|
||||||
|
headIconMap.forEach((k,v)->{
|
||||||
|
// 是否有道具
|
||||||
|
Item item = itemManager.getItem(k);
|
||||||
|
if (item != null){
|
||||||
|
// 是否使用
|
||||||
|
Long time = validTime.get(k);
|
||||||
|
if (time == null){
|
||||||
|
// 0:永久,其他到期时间
|
||||||
|
if (v.getTime() == 0){
|
||||||
|
validTime.putIfAbsent(k,0L);
|
||||||
|
}else {
|
||||||
|
validTime.putIfAbsent(k,now+v.getTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新玩家数据
|
||||||
|
user.getPlayerInfoManager().setHeadFrameValidTime(validTime);
|
||||||
|
user.getPlayerInfoManager().setOldHeadFrame(1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,23 @@ public class CBean2Proto {
|
||||||
return itemProto;
|
return itemProto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取玩家头像框列表
|
||||||
|
* @param user
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<CommonProto.HeadFrameList> getHeadFrameList(User user){
|
||||||
|
List<CommonProto.HeadFrameList> headFrameLists = new ArrayList<>();
|
||||||
|
// 获取玩家已使用头像框列表
|
||||||
|
Map<Integer, Long> validTime = user.getPlayerInfoManager().getHeadFrameValidTime();
|
||||||
|
validTime.forEach((k,v)->{
|
||||||
|
CommonProto.HeadFrameList.Builder build = CommonProto.HeadFrameList.newBuilder();
|
||||||
|
build.setHeadFrameId(k).setValidTime(v.intValue());
|
||||||
|
headFrameLists.add(build.build());
|
||||||
|
});
|
||||||
|
return headFrameLists;
|
||||||
|
}
|
||||||
|
|
||||||
public static CommonProto.Hero getHero(Hero hero){
|
public static CommonProto.Hero getHero(Hero hero){
|
||||||
List<CommonProto.SoulPos> soulPoss = new ArrayList<>();
|
List<CommonProto.SoulPos> soulPoss = new ArrayList<>();
|
||||||
Map<Integer, Integer> soulEquipMap = hero.getSoulEquipByPositionMap();
|
Map<Integer, Integer> soulEquipMap = hero.getSoulEquipByPositionMap();
|
||||||
|
@ -168,7 +185,7 @@ public class CBean2Proto {
|
||||||
return CommonProto.Equip.newBuilder().setEquipId(equipId).build();
|
return CommonProto.Equip.newBuilder().setEquipId(equipId).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CommonProto.Drop getDrop(TemporaryItems temporaryItems) {
|
/*public static CommonProto.Drop getDrop(TemporaryItems temporaryItems) {
|
||||||
CommonProto.Drop.Builder drop = CommonProto.Drop.newBuilder();
|
CommonProto.Drop.Builder drop = CommonProto.Drop.newBuilder();
|
||||||
for (Item item : temporaryItems.items.values()) {
|
for (Item item : temporaryItems.items.values()) {
|
||||||
drop.addItemlist(getItem(item,-1));
|
drop.addItemlist(getItem(item,-1));
|
||||||
|
@ -178,7 +195,7 @@ public class CBean2Proto {
|
||||||
}
|
}
|
||||||
|
|
||||||
return drop.build();
|
return drop.build();
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private static List<CommonProto.SpecialEffects> parseFromMap(Map<Integer,Integer> propertyValueByIdMap){
|
private static List<CommonProto.SpecialEffects> parseFromMap(Map<Integer,Integer> propertyValueByIdMap){
|
||||||
List<CommonProto.SpecialEffects> result = new ArrayList<>();
|
List<CommonProto.SpecialEffects> result = new ArrayList<>();
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package config;
|
||||||
|
|
||||||
|
import manager.STableManager;
|
||||||
|
import manager.Table;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Table(name ="PlayerHeadIcon")
|
||||||
|
public class SPlayerHeadIcon implements BaseConfig {
|
||||||
|
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
private int itemId;
|
||||||
|
|
||||||
|
private int type;
|
||||||
|
|
||||||
|
private int time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 头像框id,头像框类
|
||||||
|
*/
|
||||||
|
private static Map<Integer,SPlayerHeadIcon> headIconMap = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* 类型,头像框id,头像框类
|
||||||
|
*/
|
||||||
|
private static Map<Integer,Map<Integer,SPlayerHeadIcon>> headIconMapByType = new HashMap<>();
|
||||||
|
|
||||||
|
// 头像框类型
|
||||||
|
public static int HeadFrameType = 2;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() throws Exception {
|
||||||
|
Map<Integer, SPlayerHeadIcon> config = STableManager.getConfig(SPlayerHeadIcon.class);
|
||||||
|
Map<Integer,SPlayerHeadIcon> map = new HashMap<>();
|
||||||
|
Map<Integer,Map<Integer,SPlayerHeadIcon>> typeMap = new HashMap<>();
|
||||||
|
config.forEach((k,v)->{
|
||||||
|
map.putIfAbsent(v.getItemId(),v);
|
||||||
|
typeMap.putIfAbsent(v.getType(),new HashMap<>());
|
||||||
|
typeMap.get(v.getType()).putIfAbsent(v.getItemId(),v);
|
||||||
|
});
|
||||||
|
headIconMap = map;
|
||||||
|
headIconMapByType = typeMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemId() {
|
||||||
|
return itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, SPlayerHeadIcon> getHeadIconMap() {
|
||||||
|
return headIconMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, Map<Integer, SPlayerHeadIcon>> getHeadIconMapByType() {
|
||||||
|
return headIconMapByType;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue