Я у себя чутка переписал StatsChangeRecorder. Работает довольно эффективно и без фризов. Правда, у меня ХФ. Можно примерно понять логику и порядок отправки пакетов. Т.е вручную из основного кода дергается только метод sendChanges().Всем привет ) по поводу обновления регенерации hp,mp,cp, слать пакет каждые 3 сек UserInfo жирно, я вот задумался о StatusUpdate, но столкнулся со следующим, если я шлю 1 стат на обнову, он попросту игнорируется клиентом, если добавляю второй стат скажем ХП и МП в момент получения пакета, идет микрофриз и статы клиент обновляет. Но тем же пакетом я обновляю вес инвентаря, и шлю 1 стат, и он обновляется без фризов. Попробовал вместо StatusUpdate слать каждые 3 сек UserInfo, и все четко обновляет без фризов, хотя пакет намного больше ) Может не правильно шлю статы? или нужно в пакете слать для обновления ХП определенные статы?
package ru.nts.gameserver.game.model;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ru.nts.core.config.core.ServerConfig;
import ru.nts.core.config.gameplay.OtherConfig;
import ru.nts.core.network.outgoing.*;
import ru.nts.gameserver.game.instancemanager.CursedWeaponsManager;
import ru.nts.gameserver.game.instancemanager.PartyRoomManager;
import ru.nts.gameserver.game.model.entity.duel.Duel;
import ru.nts.gameserver.game.model.enums.EPrivateStoreType;
import ru.nts.gameserver.game.skills.Skill;
import ru.nts.gameserver.game.stats.base.Stats;
import ru.nts.gameserver.system.enums.ClassID;
import ru.nts.gameserver.system.enums.FutureType;
import ru.nts.gameserver.system.interfaces.IGConstant;
import ru.nts.gameserver.system.interfaces.IVars;
import ru.nts.gameserver.system.world.World;
import ru.nts.gameserver.system.world.WorldRegion;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Aristo
* @since 11.07.2024
*/
@Slf4j
public class StatsChangeRecorder {
private final int player_object_id;
private String title = null;
//Пепердолы. Их проверяем в первую очередь, т.к если что-то из этого поменялось, то остальное уже можно не проверять.
private final int[] publicPaperDoll = new int[PcInventory.PLAYER_PUBLIC_INFO_PAPERDOLL_ORDER.length];
private final int[] privatePaperDoll = new int[PcInventory.PLAYER_PRIVATE_INFO_PAPERDOLL_ORDER.length];
private final SavedPublicInfo savedPublicInfo = new SavedPublicInfo();
private final SavedPrivateInfo savedPrivateInfo = new SavedPrivateInfo();
private final SavedStatusUpdate savedStatusUpdate = new SavedStatusUpdate();
private final SavedEtcStatusUpdate savedEtcStatusUpdate = new SavedEtcStatusUpdate();
private final SavedStorageMaxCount savedStorageMaxCount = new SavedStorageMaxCount();
private final AtomicBoolean refreshing = new AtomicBoolean(false);
public StatsChangeRecorder(Player player) {
this.player_object_id = player.getObjectId();
}
/**
* Основной метод автоматизации определения необходимости отсылать изменения информации в клиент.
* ВАЖНО: Сохраняемые тут данные никак не влияют на то, что отправляется в клиент.
* Они выполняют только роль снапшота и позволяют определить факт изменений в определенной категории информации и необходимость ее обновления в клиенте.
* При отправке пакетов, они заполняются актуальными данными.
*/
public void sendChanges() {
if (getPlayer() instanceof Player player && (player.isReal() || !player.isInPowerSaveMode())) { //Ботам в неактивных регионах не обновляем никогда
if (refreshing.compareAndSet(false,true)) {
try {
boolean needTitleRefresh = false;
boolean needStatusRefresh = false;
boolean needEtcStatusRefresh = false;
boolean needStorageRefresh = false;
boolean needPrivateRefresh = false;
boolean needPublicRefresh = false;
//Проверяем наличие отложенных тасков на рассылку инфы. Если они есть, то все связанные проверки можно скипать.
final boolean publicInfoBroadcastAlreadyRequested = player.hasTask(FutureType.PLAYER_BROADCAST_PUBLIC_INFO_TASK);
final boolean privateInfoSendingAlreadyRequested = player.hasTask(FutureType.PLAYER_SEND_PRIVATE_INFO_TASK);
//Если отложенный таск на обновление статусов уже есть, то повторные проверки можно скипать, т.к они уже были отправлены менее 100мс назад.
final boolean publicInfoRefreshAlreadyRequested = player.hasTask(FutureType.PLAYER_REFRESH_PUBLIC_STATS_TASK);
final boolean privateInfoRefreshAlreadyRequested = player.isFake() || player.hasTask(FutureType.PLAYER_REFRESH_PRIVATE_STATS_TASK); //Ботам не обновляем никогда
final boolean statusRefreshAlreadyRequested = player.hasTask(FutureType.PLAYER_REFRESH_STATUS_STATS_TASK);
final boolean etcStatusRefreshAlreadyRequested = player.isFake() || player.hasTask(FutureType.PLAYER_REFRESH_ETC_STATUS_STATS_TASK); //Ботам не обновляем никогда
final boolean titleRefreshAlreadyRequested = player.hasTask(FutureType.PLAYER_REFRESH_TITLE_STATS_TASK);
final boolean storageRefreshAlreadyRequested = player.isFake() || player.hasTask(FutureType.PLAYER_REFRESH_STORAGE_STATS_TASK); //Ботам не обновляем никогда
//Фактически, рассылка тут не производится, а только запрашивается.
boolean publicInfoChanged = !publicInfoBroadcastAlreadyRequested && sendPublicInfoChanges();
//Если ничего не поменялось в публичной инфе и нет отложенного таска, то пробуем забродкастить изменения статуса.
if (!publicInfoChanged && !publicInfoBroadcastAlreadyRequested) {
if (!statusRefreshAlreadyRequested && broadcastPublicStatusUpdate()) {
needStatusRefresh = true;
}
} else {
needPublicRefresh = true;
needStatusRefresh = true;
}
//Фактически, рассылка тут не производится, а только запрашивается.
boolean privateInfoChanged = !privateInfoSendingAlreadyRequested && sendPrivateInfoChanges(publicInfoChanged);
//Если ничего не поменялось в приватной инфе и нет отложенного таска, то пробуем отослать изменения статуса.
if (!privateInfoChanged && !privateInfoSendingAlreadyRequested) {
if (!statusRefreshAlreadyRequested && sendPrivateStatusUpdate()) {
needStatusRefresh = true;
}
} else {
needPrivateRefresh = true;
needStatusRefresh = true;
}
//Проверяем наличие существующих запросов на обновление статов. Если они есть, то все связанные проверки можно скипать.
if (!statusRefreshAlreadyRequested && broadcastPartyStatusUpdate()) {
needStatusRefresh = true;
}
if (!statusRefreshAlreadyRequested && broadcastOlympiadStatusUpdate()) {
needStatusRefresh = true;
}
if (!statusRefreshAlreadyRequested && broadcastDuelStatusUpdate()) {
needStatusRefresh = true;
}
if (!etcStatusRefreshAlreadyRequested && sendEtcStatus()) {
needEtcStatusRefresh = true;
}
if (!storageRefreshAlreadyRequested && sendStorageMaxCountChanges()) {
needStorageRefresh = true;
}
if (!titleRefreshAlreadyRequested && sendTitleChanges()) {
needTitleRefresh = true;
}
//Запускаем таски на обновление статов по итогам обнаружения изменений в сохраненных данных.
if (needPublicRefresh) {
player.addTaskForce(FutureType.PLAYER_REFRESH_PUBLIC_STATS_TASK, this::refreshPublicSaves, ServerConfig.PLAYER_PACKET_CACHE_LIFE_TIME);
}
if (needPrivateRefresh) {
player.addTaskForce(FutureType.PLAYER_REFRESH_PRIVATE_STATS_TASK, this::refreshPrivateSaves, ServerConfig.PLAYER_PACKET_CACHE_LIFE_TIME);
}
if (needStatusRefresh) {
player.addTaskForce(FutureType.PLAYER_REFRESH_STATUS_STATS_TASK, this::refreshStatusSaves, ServerConfig.PLAYER_PACKET_CACHE_LIFE_TIME);
}
if (needEtcStatusRefresh) {
player.addTaskForce(FutureType.PLAYER_REFRESH_ETC_STATUS_STATS_TASK, this::refreshEtcStatusSaves, ServerConfig.PLAYER_PACKET_CACHE_LIFE_TIME);
}
if (needTitleRefresh) {
player.addTaskForce(FutureType.PLAYER_REFRESH_TITLE_STATS_TASK, this::refreshTitleSaves, ServerConfig.PLAYER_PACKET_CACHE_LIFE_TIME);
}
if (needStorageRefresh) {
player.addTaskForce(FutureType.PLAYER_REFRESH_STORAGE_STATS_TASK, this::refreshStorageSaves, ServerConfig.PLAYER_PACKET_CACHE_LIFE_TIME);
}
} finally {
refreshing.compareAndSet(true,false);
}
}
}
}
private Player getPlayer() {
return World.getInstance().getPlayerByObjectID(player_object_id);
}
private void refreshTitleSaves() {
if (getPlayer() instanceof Player p && p.getInventory() instanceof Inventory inv) {
title = p.getTitle();
}
}
private void refreshStatusSaves() {
if (getPlayer() instanceof Player p && p.getInventory() instanceof Inventory inv) {
savedStatusUpdate.fill(p);
}
}
private void refreshEtcStatusSaves() {
if (getPlayer() instanceof Player p) {
savedEtcStatusUpdate.fill(p);
}
}
private void refreshStorageSaves() {
if (getPlayer() instanceof Player p && p.getInventory() instanceof Inventory inv) {
savedStorageMaxCount.fill(p);
}
}
private void refreshPublicSaves() {
if (getPlayer() instanceof Player p && p.getInventory() instanceof Inventory inv) {
for (int i = 0; i < PcInventory.PLAYER_PUBLIC_INFO_PAPERDOLL_ORDER.length; i++) {
publicPaperDoll[i] = inv.getPaperdollObjectId(PcInventory.PLAYER_PUBLIC_INFO_PAPERDOLL_ORDER[i]);
}
savedPublicInfo.fill(p);
}
}
private void refreshPrivateSaves() {
if (getPlayer() instanceof Player p && p.getInventory() instanceof Inventory inv) {
for (int i = 0; i < PcInventory.PLAYER_PRIVATE_INFO_PAPERDOLL_ORDER.length; i++) {
privatePaperDoll[i] = inv.getPaperdollObjectId(PcInventory.PLAYER_PRIVATE_INFO_PAPERDOLL_ORDER[i]);
}
savedPrivateInfo.fill(p);
}
}
private boolean sendPrivateStatusUpdate() {
if (getPlayer() instanceof Player p && p.isReal()) {
S_STATUS_UPDATE statusUpdate = savedStatusUpdate.makePrivateStatusUpdate(p);
if (statusUpdate != null && statusUpdate.hasAttributes()) {
p.sendPacket(statusUpdate);
return true;
}
}
return false;
}
private boolean broadcastPublicStatusUpdate() {
if (getPlayer() instanceof Player p) {
S_STATUS_UPDATE statusUpdate = savedStatusUpdate.makePublicStatusUpdate(p);
if (statusUpdate != null && statusUpdate.hasAttributes()) {
p.broadcastPacket(statusUpdate);
return true;
}
}
return false;
}
private boolean broadcastPartyStatusUpdate() {
if (getPlayer() instanceof Player p && (p.isReal() || p instanceof FakePlayer f && f.isSlave())) {
Party party = p.getParty();
if (party != null && party.getPartyRealPlayersCount() > 1) {
S_PARTY_SMALL_WINDOW_UPDATE partyUpdate = savedStatusUpdate.makePartyStatusUpdate(p);
if (partyUpdate != null) {
party.broadcastToPartyMembersExclude(p, partyUpdate);
return true;
}
}
}
return false;
}
private boolean broadcastOlympiadStatusUpdate() {
if (getPlayer() instanceof Player p && (p.isReal() || p instanceof FakePlayer f && f.isSlave())) {
if (p.isInOlympiadMode() && p.isOlympiadStart()) {
S_EX_OLYMPIAD_USER_INFO olympiadUpdate = savedStatusUpdate.makeOlympiadStatusUpdate(p, p.getOlympiadSide());
if (olympiadUpdate != null) {
p.broadcastPacket(olympiadUpdate);
return true;
}
}
}
return false;
}
private boolean broadcastDuelStatusUpdate() {
if (getPlayer() instanceof Player p && (p.isReal() || p instanceof FakePlayer f && f.isSlave())) {
if (p.isInDuel() && (p.getDuelState() == IGConstant.DUELSTATE_DUELLING || p.getDuelState() == IGConstant.DUELSTATE_DEAD)) {
S_EX_DUEL_UPDATE_USER_INFO duelUpdate = savedStatusUpdate.makeDuelStatusUpdate(p);
if (duelUpdate != null && p.getDuel() instanceof Duel duel) {
if (p.getDuelSide() == 1) {
duel.broadcastToTeam2(duelUpdate);
} else if (p.getDuelSide() == 2) {
duel.broadcastToTeam1(duelUpdate);
}
return true;
}
}
}
return false;
}
private boolean sendTitleChanges() {
if (getPlayer() instanceof Player p) {
if (title == null && p.getTitle() != null || title != null && !title.equals(p.getTitle())) {
p.broadcastPacketToOthers(new S_NICKNAME_CHANGED(p));
return true;
}
}
return false;
}
private boolean sendPrivateInfoChanges(boolean publicInfoChanged) {
if (getPlayer() instanceof Player p && p.isReal()) {
if (publicInfoChanged || privatePapperDollChanged() || privateInfoChanged()) {
p.requestSendPlayerPrivateInfo();
return true;
}
}
return false;
}
private boolean sendPublicInfoChanges() {
if (getPlayer() instanceof Player p) {
if (publicPapperDollChanged() || publicInfoChanged()) {
p.requestBroadcastPlayerInfo();
return true;
}
}
return false;
}
private boolean sendEtcStatus() {
if (getPlayer() instanceof Player p) {
S_ETC_STATUS_UPDATE etcStatusUpdate = savedEtcStatusUpdate.makeEtcStatusUpdate(p);
if (etcStatusUpdate != null) {
p.sendPacket(etcStatusUpdate);
return true;
}
}
return false;
}
private boolean sendStorageMaxCountChanges() {
if (getPlayer() instanceof Player p) {
S_EX_STORAGE_MAX_COUNT storageMaxCount = savedStorageMaxCount.makeStorageMaxCountUpdate(p);
if (storageMaxCount != null) {
p.sendPacket(storageMaxCount);
return true;
}
}
return false;
}
private boolean privatePapperDollChanged() {
if (getPlayer() instanceof Player p && p.isReal()) {
boolean changed = false;
for (int i = 0; i < PcInventory.PLAYER_PRIVATE_INFO_PAPERDOLL_ORDER.length; i++) {
if (p.getInventory().getPaperdollObjectId(PcInventory.PLAYER_PRIVATE_INFO_PAPERDOLL_ORDER[i]) != privatePaperDoll[i]) {
changed = true;
break;
}
}
return changed;
}
return false;
}
private boolean publicPapperDollChanged() {
if (getPlayer() instanceof Player p) {
boolean changed = false;
for (int i = 0; i < PcInventory.PLAYER_PUBLIC_INFO_PAPERDOLL_ORDER.length; i++) {
if (p.getInventory().getPaperdollObjectId(PcInventory.PLAYER_PUBLIC_INFO_PAPERDOLL_ORDER[i]) != publicPaperDoll[i]) {
changed = true;
break;
}
}
return changed;
}
return false;
}
private boolean publicInfoChanged() {
if (getPlayer() instanceof Player p) {
return !savedPublicInfo.equals(new SavedPublicInfo().fill(p));
}
return false;
}
private boolean privateInfoChanged() {
if (getPlayer() instanceof Player p && p.isReal()) {
return !savedPrivateInfo.equals(new SavedPrivateInfo().fill(p));
}
return false;
}
//Общие параметры. Могут быть как публичными, так и приватными.
//Если тут что-то поменялось, то нужно обновлять и приватную инфу.
@NoArgsConstructor
private static class SavedPublicInfo {
private WorldRegion region;
private String name;
private String title;
private int race;
private int sex;
private int expand_deco_slot;
private int cloak;
private int recomendation_points_received;
private int vehicle_game_object_id;
private double movement_speed_mul;
private double physical_attack_speed_mul;
private double collision_radius;
private double collision_height;
private int hair_style;
private int hair_color;
private int face;
private int abnormal_visual_effects;
private int abnormal_visual_effects_special;
private int clan_id;
private int clan_crest_id;
private int large_clan_crest_id;
private int ally_id;
private int ally_crest_id;
private int class_id;
private int sitting;
private int running;
private int combat;
private int like_dead;
private int invisible;
private EPrivateStoreType private_store_type;
private int weapon_enchant_effect;
private int team;
private int noble;
private int hero;
private int fishing;
private int mount_type;
private int pledge_class;
private int pledge_type;
private int clan_rep_score;
private int cursed_weapon_level;
private int mount_id;
private int name_color;
private int title_color;
private int transformation;
private int agathion;
private int[] cubics_id;
private int party_room_active;
private int floating;
private int battlefield_penalty;
private int mounted;
public synchronized SavedPublicInfo fill(Player player) {
if (player instanceof Player p) {
try {
region = p.getWorldRegion();
sitting = p.isSitting() ? 0 : 1;
combat = p.isInCombat() ? 1 : 0;
like_dead = p.isDead() ? 1 : 0;
invisible = p.isInvisible() ? 1 : 0;
recomendation_points_received = p.getRecSystem().getRecommendsHave();
if (clan_id > 0 && p.getClanId() != 0) {
clan_rep_score = p.getClan().getReputationScore();
} else {
clan_rep_score = 0;
}
if (p.isCursedWeaponEquipped()) {
name = p.getTransformationName();
clan_crest_id = 0;
ally_crest_id = 0;
large_clan_crest_id = 0;
cursed_weapon_level = CursedWeaponsManager.getInstance().getLevel(p.getCursedWeaponEquippedId());
} else {
name = p.getName();
clan_crest_id = p.getClanCrestId();
ally_crest_id = p.getAllyCrestId();
large_clan_crest_id = p.getClanCrestLargeId();
cursed_weapon_level = 0;
}
if (p.getMountEngine().isMounted()) {
weapon_enchant_effect = 0;
if (p.getTransformation() != 0) {
mount_id = 0;
mount_type = 0;
} else {
mount_id = p.getMountEngine().getMountNpcId() + 1000000;
mount_type = p.getMountEngine().getYongmaType();
}
} else {
weapon_enchant_effect = p.getEnchantEffect();
mount_id = 0;
mount_type = 0;
}
running = p.isRunning() ? 0x01 : 0x00; //changes the Speed display on Status Window
movement_speed_mul = p.getMovementSpeedMultiplier();
vehicle_game_object_id = p.isInBoat() ? p.getVehicle().getObjectId() : 0x00;
race = p.getRace().ordinal();
sex = p.getSex();
physical_attack_speed_mul = p.getStat().getAttackSpeedMultiplier();
collision_radius = p.getCurrentColRadius();
collision_height = p.getCurrentColHeight();
hair_style = p.getHairStyle();
hair_color = p.getHairColor();
face = p.getFace();
title = p.getTitle();
clan_id = p.getClanId();
ally_id = p.getAllyId();
private_store_type = p.getPrivateStore().getType();
cubics_id = p.getCubicsID();
abnormal_visual_effects = p.getAbnormalVisualEffects();
floating = p.isSwimming() ? 1 : (p.isFlying() ? 2 : 0);
abnormal_visual_effects_special = p.getAbnormalVisualEffectSpecial();
class_id = p.getClassId().getID();
team = p.getTeam(); //team circle around feet 1= Blue, 2 = red
noble = p.isNoble() || p.isGM() && OtherConfig.GM_HERO_AURA ? 1 : 0; //0x01: symbol on char menu ctrl+I
hero = p.isHero() || p.isGM() && OtherConfig.GM_HERO_AURA ? 1 : 0; //0x01: Hero Aura and symbol
fishing = p.isFishing() ? 1 : 0; // Fishing Mode
name_color = p.getNameColor();
pledge_class = p.getSocialClass();
pledge_type = p.getPledgeType();
title_color = p.getTitleColor();
transformation = p.getTransformation();
agathion = p.getAgathionId();
party_room_active = PartyRoomManager.getInstance().isLeader(p) ? 0x01 : 0x00;
expand_deco_slot = p.getInventory().getAllowedTalismans();
cloak = p.isStatActive(Stats.CLOAK) ? 1 : 0;
battlefield_penalty = p.getEffectBySkillId(Skill.SKILL_BATTLEFIELD_PENALTY) != null ? 0 : 1;
mounted = p.getMountEngine().isMounted() ? p.getMountEngine().getMountNpcId() : 0;
} catch (Exception e) {
log.error("Cannot fill public info saves for {}", p.toIDString(), e);
}
}
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof SavedPublicInfo that) {
return region == that.region && mounted == that.mounted && race == that.race && sex == that.sex && expand_deco_slot == that.expand_deco_slot && cloak == that.cloak && recomendation_points_received == that.recomendation_points_received && vehicle_game_object_id == that.vehicle_game_object_id && Double.compare(movement_speed_mul, that.movement_speed_mul) == 0 && Double.compare(physical_attack_speed_mul, that.physical_attack_speed_mul) == 0 && Double.compare(collision_radius, that.collision_radius) == 0 && Double.compare(collision_height, that.collision_height) == 0 && hair_style == that.hair_style && hair_color == that.hair_color && face == that.face && abnormal_visual_effects == that.abnormal_visual_effects && abnormal_visual_effects_special == that.abnormal_visual_effects_special && clan_id == that.clan_id && clan_crest_id == that.clan_crest_id && large_clan_crest_id == that.large_clan_crest_id && ally_id == that.ally_id && ally_crest_id == that.ally_crest_id && class_id == that.class_id && sitting == that.sitting && running == that.running && combat == that.combat && like_dead == that.like_dead && invisible == that.invisible && weapon_enchant_effect == that.weapon_enchant_effect && team == that.team && noble == that.noble && hero == that.hero && fishing == that.fishing && mount_type == that.mount_type && pledge_class == that.pledge_class && pledge_type == that.pledge_type && clan_rep_score == that.clan_rep_score && cursed_weapon_level == that.cursed_weapon_level && mount_id == that.mount_id && name_color == that.name_color && title_color == that.title_color && transformation == that.transformation && agathion == that.agathion && party_room_active == that.party_room_active && floating == that.floating && battlefield_penalty == that.battlefield_penalty && Objects.equals(name, that.name) && Objects.equals(title, that.title) && private_store_type == that.private_store_type && Objects.deepEquals(cubics_id, that.cubics_id);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(region, mounted, name, title, race, sex, expand_deco_slot, cloak, recomendation_points_received, vehicle_game_object_id, movement_speed_mul, physical_attack_speed_mul, collision_radius, collision_height, hair_style, hair_color, face, abnormal_visual_effects, abnormal_visual_effects_special, clan_id, clan_crest_id, large_clan_crest_id, ally_id, ally_crest_id, class_id, sitting, running, combat, like_dead, invisible, private_store_type, weapon_enchant_effect, team, noble, hero, fishing, mount_type, pledge_class, pledge_type, clan_rep_score, cursed_weapon_level, mount_id, name_color, title_color, transformation, agathion, Arrays.hashCode(cubics_id), party_room_active, floating, battlefield_penalty);
}
}
//Только приватные параметры. Другие персонажи о них не знают.
@NoArgsConstructor
private static class SavedPrivateInfo {
private int level;
private long experience_points;
private int str_value;
private int con_value;
private int dex_value;
private int int_value;
private int wit_value;
private int men_value;
private int current_load;
private int maximum_load;
private int physical_attack;
private int physical_attack_speed;
private int physical_defence;
private int avoid;
private int hit;
private int critical_rate;
private int magical_attack;
private int magical_attack_speed;
private int magical_defence;
private int weapon_equipped_flag;
private int recommendation_points_remaining;
private int recommendation_points_received;
private int skill_points;
private int pledge_power;
private int inventory_limit;
private int can_crystalize;
private int pk_points;
private int pvp_points;
private int defence_attribute_fire;
private int defence_attribute_water;
private int defence_attribute_wind;
private int defence_attribute_earth;
private int defence_attribute_holy;
private int defence_attribute_unholy;
private int fame;
private int vitality;
private int territory_id;
private int disguised;
private int attack_attribute_type;
private int attack_attribute_value;
public synchronized SavedPrivateInfo fill(Player player) {
if (player instanceof Player p && p.isReal()) {
try {
level = p.getLevel();
experience_points = p.getExp();
str_value = p.getStat().getSTR();
dex_value = p.getStat().getDEX();
con_value = p.getStat().getCON();
int_value = p.getStat().getINT();
wit_value = p.getStat().getWIT();
men_value = p.getStat().getMEN();
current_load = p.getCurrentLoad();
maximum_load = p.getMaxLoad();
physical_attack = (int) p.getStat().getPAtk();
physical_attack_speed = (int) p.getStat().getPAtkSpd();
physical_defence = (int) (p.getStat().getPDef());
avoid = (int) p.getStat().getEvasionRate();
hit = (int) p.getStat().getAccuracy();
critical_rate = (int) p.getStat().getCriticalHit();
magical_attack = (int) (p.getStat().getMAtk(null));
magical_attack_speed = (int) (p.getStat().getMAtkSpd());
magical_defence = (int) (p.getStat().getMDef(null));
weapon_equipped_flag = p.getActiveWeaponInstance() != null ? 0x28 : 0x14;
skill_points = (int)p.getSp();
can_crystalize = p.calcStat(Stats.CRYSTALLIZE, 0, null, null) > 0 ? 1 : 0;
pk_points = p.getPkKills();
pvp_points = p.getPvpKills();
pledge_power = p.getClanPrivileges();
recommendation_points_remaining = p.getRecSystem().getRecommendsLeft(); //c2 recommendations remaining
recommendation_points_received = p.getRecSystem().getRecommendsHave(); //c2 recommendations received
inventory_limit = p.getInventoryLimit();
attack_attribute_type = p.getStat().getAttackElement()[0];
attack_attribute_value = p.getStat().getAttackElement()[1];
defence_attribute_fire = (int) (p.getStat().getDefenceFire());
defence_attribute_water = (int) (p.getStat().getDefenceWater());
defence_attribute_wind = (int) (p.getStat().getDefenceWind());
defence_attribute_earth = (int) (p.getStat().getDefenceEarth());
defence_attribute_holy = (int) (p.getStat().getDefenceHoly());
defence_attribute_unholy = (int) (p.getStat().getDefenceDark());
fame = p.getFame();
vitality = p.getVitality().getPoints();
disguised = p.getVarInt(IVars.USER_DISGUISED);
territory_id = p.getDominionID();
} catch (Exception e) {
log.error("Can't fill private info saves for {}", p.toIDString(), e);
}
}
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof SavedPrivateInfo that) {
return level == that.level && experience_points == that.experience_points && str_value == that.str_value && con_value == that.con_value && dex_value == that.dex_value && int_value == that.int_value && wit_value == that.wit_value && men_value == that.men_value && current_load == that.current_load && maximum_load == that.maximum_load && physical_attack == that.physical_attack && physical_attack_speed == that.physical_attack_speed && physical_defence == that.physical_defence && avoid == that.avoid && hit == that.hit && critical_rate == that.critical_rate && magical_attack == that.magical_attack && magical_attack_speed == that.magical_attack_speed && magical_defence == that.magical_defence && weapon_equipped_flag == that.weapon_equipped_flag && recommendation_points_remaining == that.recommendation_points_remaining && recommendation_points_received == that.recommendation_points_received && skill_points == that.skill_points && pledge_power == that.pledge_power && inventory_limit == that.inventory_limit && can_crystalize == that.can_crystalize && pk_points == that.pk_points && pvp_points == that.pvp_points && defence_attribute_fire == that.defence_attribute_fire && defence_attribute_water == that.defence_attribute_water && defence_attribute_wind == that.defence_attribute_wind && defence_attribute_earth == that.defence_attribute_earth && defence_attribute_holy == that.defence_attribute_holy && defence_attribute_unholy == that.defence_attribute_unholy && fame == that.fame && vitality == that.vitality && territory_id == that.territory_id && disguised == that.disguised && attack_attribute_type == that.attack_attribute_type && attack_attribute_value == that.attack_attribute_value;
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(level, experience_points, str_value, con_value, dex_value, int_value, wit_value, men_value, current_load, maximum_load, physical_attack, physical_attack_speed, physical_defence, avoid, hit, critical_rate, magical_attack, magical_attack_speed, magical_defence, weapon_equipped_flag, recommendation_points_remaining, recommendation_points_received, skill_points, pledge_power, inventory_limit, can_crystalize, pk_points, pvp_points, defence_attribute_fire, defence_attribute_water, defence_attribute_wind, defence_attribute_earth, defence_attribute_holy, defence_attribute_unholy, fame, vitality, territory_id, disguised, attack_attribute_type, attack_attribute_value);
}
}
@NoArgsConstructor
private static class SavedStatusUpdate {
//Явно изменяют значения в клиенте.
private int current_cp;
private int maximum_cp;
private int current_hp;
private int maximum_hp;
private int current_mp;
private int maximum_mp;
private int karma;
private int pvp_flag;
//Не изменяют значения или изменяют не явно.
private int level;
private long experience_points;
private int str_value;
private int con_value;
private int dex_value;
private int int_value;
private int wit_value;
private int men_value;
private int current_load;
private int maximum_load;
private int physical_attack;
private int physical_attack_speed;
private int physical_defence;
private int avoid;
private int hit;
private int critical_rate;
private int magical_attack;
private int magical_attack_speed;
private int magical_defence;
private ClassID class_id;
private String name;
public synchronized void fill(Player player) {
if (player instanceof Player p) {
try {
maximum_cp = (int) (p.getStat().getMaxCp());
current_cp = (int) (p.getCurrentCp());
current_hp = (int) p.getCurrentHp();
maximum_hp = (int) p.getStat().getMaxHp();
current_mp = (int) p.getCurrentMp();
maximum_mp = (int) p.getStat().getMaxMp();
karma = p.getKarma();
pvp_flag = p.getPvpFlag();
level = p.getLevel();
experience_points = p.getExp();
str_value = p.getStat().getSTR();
dex_value = p.getStat().getDEX();
con_value = p.getStat().getCON();
int_value = p.getStat().getINT();
wit_value = p.getStat().getWIT();
men_value = p.getStat().getMEN();
current_load = p.getCurrentLoad();
maximum_load = p.getMaxLoad();
physical_attack = (int) p.getStat().getPAtk();
physical_attack_speed = (int) p.getStat().getPAtkSpd();
physical_defence = (int) (p.getStat().getPDef());
avoid = (int) p.getStat().getEvasionRate();
hit = (int) p.getStat().getAccuracy();
critical_rate = (int) p.getStat().getCriticalHit();
magical_attack = (int) (p.getStat().getMAtk(null));
magical_attack_speed = (int) (p.getStat().getMAtkSpd());
magical_defence = (int) (p.getStat().getMDef(null));
class_id = p.getClassId();
name = p.getName();
} catch (Exception e) {
log.error("Can't fill saved status update for {}", p.toIDString(), e);
}
}
}
public synchronized S_STATUS_UPDATE makePublicStatusUpdate(Player player) {
if (player instanceof Player p) {
try {
S_STATUS_UPDATE sa = new S_STATUS_UPDATE(p.getObjectId());
if (level != p.getLevel()) {
sa.addAttribute(S_STATUS_UPDATE.LEVEL, p.getLevel());
}
if (karma != p.getKarma()) {
sa.addAttribute(S_STATUS_UPDATE.KARMA, p.getKarma());
}
if (pvp_flag != p.getPvpFlag()) {
sa.addAttribute(S_STATUS_UPDATE.PVP_FLAG, p.getPvpFlag());
}
return sa;
} catch (Exception e) {
log.error("Can't make public status update for {}", p.toIDString(), e);
}
}
return null;
}
public synchronized S_STATUS_UPDATE makePrivateStatusUpdate(Player player) {
if (player instanceof Player p) {
try {
S_STATUS_UPDATE sa = new S_STATUS_UPDATE(p.getObjectId());
//В клиенте HF эти данные обрабатываются явно.
if (current_cp != p.getCurrentCp()) {
sa.addAttribute(S_STATUS_UPDATE.CUR_CP, p.getCurrentCp());
}
if (maximum_cp != p.getStat().getMaxCp()) {
sa.addAttribute(S_STATUS_UPDATE.MAX_CP, p.getStat().getMaxCp());
}
if (current_hp != (int) (p.getCurrentHp())) {
sa.addAttribute(S_STATUS_UPDATE.CUR_HP, p.getCurrentHp());
}
if (maximum_hp != p.getStat().getMaxHp()) {
sa.addAttribute(S_STATUS_UPDATE.MAX_HP, p.getStat().getMaxHp());
}
if (current_mp != (int) (p.getCurrentMp())) {
sa.addAttribute(S_STATUS_UPDATE.CUR_MP, p.getCurrentMp());
}
if (maximum_mp != p.getStat().getMaxMp()) {
sa.addAttribute(S_STATUS_UPDATE.MAX_MP, p.getStat().getMaxMp());
}
if (karma != p.getKarma()) {
sa.addAttribute(S_STATUS_UPDATE.KARMA, p.getKarma());
}
if (pvp_flag != p.getPvpFlag()) {
sa.addAttribute(S_STATUS_UPDATE.PVP_FLAG, p.getPvpFlag());
}
//В клиенте HF эти данные не обрабатываются или обрабатываются не явно.
if (level != p.getLevel()) {
sa.addAttribute(S_STATUS_UPDATE.LEVEL, p.getLevel());
}
if (experience_points != p.getExp()) {
sa.addAttribute(S_STATUS_UPDATE.EXP, p.getExp());
}
if (str_value != p.getStat().getSTR()) {
sa.addAttribute(S_STATUS_UPDATE.STR, p.getStat().getSTR());
}
if (dex_value != p.getStat().getDEX()) {
sa.addAttribute(S_STATUS_UPDATE.DEX, p.getStat().getDEX());
}
if (con_value != p.getStat().getCON()) {
sa.addAttribute(S_STATUS_UPDATE.CON, p.getStat().getCON());
}
if (int_value != p.getStat().getINT()) {
sa.addAttribute(S_STATUS_UPDATE.INT, p.getStat().getINT());
}
if (wit_value != p.getStat().getWIT()) {
sa.addAttribute(S_STATUS_UPDATE.WIT, p.getStat().getWIT());
}
if (men_value != p.getStat().getMEN()) {
sa.addAttribute(S_STATUS_UPDATE.MEN, p.getStat().getMEN());
}
if (current_load != p.getCurrentLoad()) {
sa.addAttribute(S_STATUS_UPDATE.CUR_LOAD, p.getCurrentLoad());
}
if (maximum_load != p.getMaxLoad()) {
sa.addAttribute(S_STATUS_UPDATE.MAX_LOAD, p.getMaxLoad());
}
if (physical_attack != p.getStat().getPAtk()) {
sa.addAttribute(S_STATUS_UPDATE.P_ATK, p.getStat().getPAtk());
}
if (physical_attack_speed != p.getStat().getPAtkSpd()) {
sa.addAttribute(S_STATUS_UPDATE.ATK_SPD, p.getStat().getPAtkSpd());
}
if (physical_defence != p.getStat().getPDef()) {
sa.addAttribute(S_STATUS_UPDATE.P_DEF, p.getStat().getPDef());
}
if (avoid != p.getStat().getEvasionRate()) {
sa.addAttribute(S_STATUS_UPDATE.EVASION, p.getStat().getAccuracy());
}
if (hit != p.getStat().getAccuracy()) {
sa.addAttribute(S_STATUS_UPDATE.ACCURACY, p.getStat().getAccuracy());
}
if (critical_rate != p.getStat().getCriticalHit()) {
sa.addAttribute(S_STATUS_UPDATE.CRITICAL, p.getStat().getCriticalHit());
}
if (magical_attack != p.getStat().getMAtk(null)) {
sa.addAttribute(S_STATUS_UPDATE.M_ATK, p.getStat().getMAtk(null));
}
if (magical_attack_speed != p.getStat().getMAtkSpd()) {
sa.addAttribute(S_STATUS_UPDATE.CAST_SPD, p.getStat().getMAtkSpd());
}
if (magical_defence != p.getStat().getMDef(null)) {
sa.addAttribute(S_STATUS_UPDATE.M_DEF, p.getStat().getMDef(null));
}
return sa;
} catch (Exception e) {
log.error("Can't make private status update for {}", p.toIDString(), e);
}
}
return null;
}
public synchronized S_PARTY_SMALL_WINDOW_UPDATE makePartyStatusUpdate(Player player) {
if (player instanceof Player p) {
try {
boolean update = false;
if (level != p.getLevel()) {
update = true;
}
if (!update && current_hp != (int) (p.getCurrentHp())) {
update = true;
}
if (!update && maximum_hp != p.getStat().getMaxHp()) {
update = true;
}
if (!update && current_mp != (int) (p.getCurrentMp())) {
update = true;
}
if (!update && maximum_mp != p.getStat().getMaxMp()) {
update = true;
}
if (!update && current_cp != p.getCurrentCp()) {
update = true;
}
if (!update && maximum_cp != p.getStat().getMaxCp()) {
update = true;
}
if (!update && class_id != p.getClassId()) {
update = true;
}
if (!update && name != p.getName()) {
update = true;
}
if (update) {
return new S_PARTY_SMALL_WINDOW_UPDATE(p);
}
} catch (Exception e) {
log.error("Can't make party status update for {}", p.toIDString(), e);
}
}
return null;
}
public synchronized S_EX_OLYMPIAD_USER_INFO makeOlympiadStatusUpdate(Player player, int side) {
if (player instanceof Player p) {
try {
boolean update = false;
if (level != p.getLevel()) {
update = true;
}
if (!update && current_hp != (int) (p.getCurrentHp())) {
update = true;
}
if (!update && maximum_hp != p.getStat().getMaxHp()) {
update = true;
}
if (!update && current_cp != p.getCurrentCp()) {
update = true;
}
if (!update && maximum_cp != p.getStat().getMaxCp()) {
update = true;
}
if (!update && class_id != p.getClassId()) {
update = true;
}
if (!update && name != p.getName()) {
update = true;
}
if (update) {
return new S_EX_OLYMPIAD_USER_INFO(p, side);
}
} catch (Exception e) {
log.error("Can't make olympiad status update for {}", p.toIDString(), e);
}
}
return null;
}
public synchronized S_EX_DUEL_UPDATE_USER_INFO makeDuelStatusUpdate(Player player) {
if (player instanceof Player p) {
try {
boolean update = false;
if (level != p.getLevel()) {
update = true;
}
if (!update && current_hp != (int) (p.getCurrentHp())) {
update = true;
}
if (!update && maximum_hp != p.getStat().getMaxHp()) {
update = true;
}
if (!update && current_mp != (int) (p.getCurrentMp())) {
update = true;
}
if (!update && maximum_mp != p.getStat().getMaxMp()) {
update = true;
}
if (!update && current_cp != p.getCurrentCp()) {
update = true;
}
if (!update && maximum_cp != p.getStat().getMaxCp()) {
update = true;
}
if (!update && class_id != p.getClassId()) {
update = true;
}
if (!update && name != p.getName()) {
update = true;
}
if (update) {
return new S_EX_DUEL_UPDATE_USER_INFO(p);
}
} catch (Exception e) {
log.error("Can't make duel status update for {}", p.toIDString(), e);
}
}
return null;
}
}
@NoArgsConstructor
private static class SavedEtcStatusUpdate {
private int increasedForce;
private int weightPenalty;
private int messageRefusal;
private int dangerArea;
private int weaponPenalty;
private int armorPenalty;
private int charmOfCourage;
private int deathPenaltyLevel;
private int consumedSouls;
public synchronized void fill(Player player) {
if (player instanceof Player p) {
try {
increasedForce = p.getEnergySaved();
weightPenalty = p.getWeightPenalty();
messageRefusal = p.getMessageRefusal() || p.getNoChannel() != 0 ? 1 : 0;
dangerArea = p.isInAreaDanger() ? 1 : 0;
weaponPenalty = p.getWeaponExpertisePenalty();
armorPenalty = p.getArmourExpertisePenalty();
charmOfCourage = p.isCharmOfCourage() ? 1 : 0;
deathPenaltyLevel = p.getDeathPenalty() != null ? p.getDeathPenalty().getLevel() : 0;
consumedSouls = p.getSoulSaved();
} catch (Exception e) {
log.error("Can't fill etc status saves for {}", p.toIDString(), e);
}
}
}
public synchronized S_ETC_STATUS_UPDATE makeEtcStatusUpdate(Player player) {
if (player instanceof Player p) {
try {
boolean update = false;
if (consumedSouls != p.getSoulSaved()) {
update = true;
}
if (!update && increasedForce != p.getEnergySaved()) {
update = true;
}
if (!update && weightPenalty != p.getWeightPenalty()) {
update = true;
}
if (!update && messageRefusal != (p.getMessageRefusal() || p.getNoChannel() != 0 ? 1 : 0)) {
update = true;
}
if (!update && dangerArea != (p.isInAreaDanger() ? 1 : 0)) {
update = true;
}
if (!update && weaponPenalty != p.getWeaponExpertisePenalty()) {
update = true;
}
if (!update && armorPenalty != p.getArmourExpertisePenalty()) {
update = true;
}
if (!update && charmOfCourage != (p.isCharmOfCourage() ? 1 : 0)) {
update = true;
}
if (!update && deathPenaltyLevel != (p.getDeathPenalty() != null ? p.getDeathPenalty().getLevel() : 0)) {
update = true;
}
if (update) {
return new S_ETC_STATUS_UPDATE(p);
}
} catch (Exception e) {
log.error("Can't make etc status update for {}", p.toIDString(), e);
}
}
return null;
}
}
@NoArgsConstructor
private static class SavedStorageMaxCount {
private int inventory_limit;
private int warehouse_limit;
private int trade_sell_limit;
private int dwarven_recipe_limit;
private int common_recipe_limit;
private int freight_limit;
private int trade_buy_limit;
private int inventory_extra_slots;
public synchronized void fill(Player player) {
if (player instanceof Player p && p.isReal()) {
try {
inventory_limit = p.getInventoryLimit();
warehouse_limit = p.getWarehouseLimit();
trade_sell_limit = p.getTradeSellLimit();
dwarven_recipe_limit = p.getDwarvenRecipeLimit();
common_recipe_limit = p.getCommonRecipeLimit();
freight_limit = p.getFreightLimit();
trade_buy_limit = p.getTradeBuyLimit();
inventory_extra_slots = (int) p.calcStat(Stats.INVENTORY_LIMIT, 0, null, null);
} catch (Exception e) {
log.error("Can't fill storage max count for {}", p.toIDString(), e);
}
}
}
public synchronized S_EX_STORAGE_MAX_COUNT makeStorageMaxCountUpdate(Player player) {
if (player instanceof Player p) {
try {
boolean update = false;
if (inventory_limit != p.getInventoryLimit()) {
update = true;
}
if (!update && warehouse_limit != p.getWarehouseLimit()) {
update = true;
}
if (!update && trade_sell_limit != p.getTradeSellLimit()) {
update = true;
}
if (!update && dwarven_recipe_limit != p.getDwarvenRecipeLimit()) {
update = true;
}
if (!update && common_recipe_limit != p.getCommonRecipeLimit()) {
update = true;
}
if (!update && freight_limit != p.getFreightLimit()) {
update = true;
}
if (!update && trade_buy_limit != p.getTradeBuyLimit()) {
update = true;
}
if (!update && inventory_extra_slots != (int) p.calcStat(Stats.INVENTORY_LIMIT, 0, null, null)) {
update = true;
}
if (update) {
return new S_EX_STORAGE_MAX_COUNT(p);
}
} catch (Exception e) {
log.error("Can't make storage max count update for {}", p.toIDString(), e);
}
}
return null;
}
}
}
ну получается каждые 3 сек шлю пакет StatusUpdate c параметром текущего веса инвентаря, и для наглядности добавил рандомное значение, так вот, не одно фриза, все гладко работает. Вот именно проблема с обновлением HP,MP,CPJava:character.getCharacterStatus().addUpdate(StatusType.CURRENT_WEIGHT, character.getInventory().getCurrentWeight() + Random.nextInt(50000));
CUR_HP, MAX_HP, CUR_MP, MAX_MP, CUR_CP, MAX_CP
CUR_HP, MAX_HP
package l2p.gameserver.enums;
import java.util.function.Function;
import l2p.gameserver.model.Creature;
public enum StatusUpdateType
{
LEVEL(0x01, Creature::getLevel),
EXP(0x02, creature -> (int) creature.getExp()),
STR(0x03, Creature::getSTR),
DEX(0x04, Creature::getDEX),
CON(0x05, Creature::getCON),
INT(0x06, Creature::getINT),
WIT(0x07, Creature::getWIT),
MEN(0x08, Creature::getMEN),
/**
* Даный параметр отсылается оффом в паре с MAX_HP, сначала CUR_HP, потом MAX_HP
*/
CUR_HP(0x09, creature -> (int) creature.getCurrentHp()),
MAX_HP(0x0A, Creature::getMaxHp),
/**
* Даный параметр отсылается оффом в паре с MAX_MP, сначала CUR_MP, потом MAX_MP
*/
CUR_MP(0x0B, creature -> (int) creature.getCurrentMp()),
MAX_MP(0x0C, Creature::getMaxMp),
SP(0x0D, creature -> (int) creature.getSp()),
/**
* Меняется отображение только в инвентаре, для статуса требуется UserInfo
*/
CUR_LOAD(0x0E, Creature::getCurrentWeight),
MAX_LOAD(0x0F, Creature::getWeightLimit),
P_ATK(0x11, creature -> creature.getPAtk(null, null)),
ATK_SPEED(0x12, Creature::getPAtkSpd),
P_DEF(0x13, creature -> creature.getPDef(null, null)),
EVASION(0x14, creature -> creature.getPEvasion(null)),
ACCURACY(0x15, creature -> creature.getPAccuracy()),
CRITICAL(0x16, creature -> creature.getCriticalHit(null, null)),
M_ATK(0x17, creature -> creature.getMAtk(null, null)),
CAST_SPEED(0x18, Creature::getMAtkSpd),
M_DEF(0x19, creature -> creature.getMDef(null, null)),
PVP_FLAG(0x1A, creature -> (int) creature.getPvpFlag()),
KARMA(0x1B, creature -> creature.isPlayer() ? creature.getPlayer().getKarma() : 0),
/**
* Даный параметр отсылается оффом в паре с MAX_CP, сначала CUR_CP, потом MAX_CP
*/
CUR_CP(0x21, creature -> (int) creature.getCurrentCp()),
MAX_CP(0x22, Creature::getMaxCp),
LUC(0x26, Creature::getLUC),
CHA(0x27, Creature::getCHA);
private int _id;
private Function<Creature, Integer> _value;
StatusUpdateType(int id, Function<Creature, Integer> value)
{
_id = id;
_value = value;
}
public int getId()
{
return _id;
}
public int getValue(Creature creature)
{
return _value.apply(creature).intValue();
}
}
final int idWorld = character.getIdWorld();
if (currentHp >= maxHp && currentMp >= maxMp && currentCp >= maxCp) {
stopRegeneration();
return;
}
if (currentCp < maxCp) {
final StatusUpdate statusUpdate = new StatusUpdate(idWorld);
final int newCurrentCp = (int) (currentCp + Math.max(1, cpRegen));
if (newCurrentCp >= maxCp) {
currentCp = maxCp;
} else {
currentCp = newCurrentCp;
}
statusUpdate.addUpdate(StatusType.CURRENT_CP, (int) currentCp);
character.sendPacket(statusUpdate);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
if (currentHp < maxHp || currentMp < maxMp) {
final StatusUpdate statusUpdate = new StatusUpdate(idWorld);
if (currentHp < maxHp ) {
final int newCurrentHp = (int) (currentHp + Math.max(1, hpRegen));
if (newCurrentHp >= maxHp) {
currentHp = maxHp;
} else {
currentHp = newCurrentHp;
}
statusUpdate.addUpdate(StatusType.CURRENT_HP, (int) currentHp);
}
if (currentMp < maxMp ) {
final int newCurrentMp = (int) (currentMp + Math.max(1, mpRegen));
if (newCurrentMp >= maxMp) {
currentMp = maxMp;
} else {
currentMp = newCurrentMp;
}
statusUpdate.addUpdate(StatusType.CURRENT_MP, (int) currentMp);
}
character.sendPacket(statusUpdate);
}
Серверу по барабану, sleep не будет жрать такты процессора.Или все таки серверу будет легче запускать второй таск чем подождать одну десятую секунды?
regTask = Threads.getInstance().scheduleAtFixedRate(this::doRegeneration, 1000, 3000);
А как насчет виртуальных потоков в Яве? Я не пробовал, но из того что читал sleep должен переключить task на тот который можно будет делать. А потом все вроде переключится обратно.Серверу по барабану, sleep не будет жрать такты процессора.
Другое дело, что вы потенциально блокируете IO как минимум текущего клиента, как максимум всех клиентов (например, если все потоки в тред пуле встали в этот sleep). В итоге это будет выглядеть как жуткие лаги.
Рекомендую обратить внимание на неблокирующие операции. C Java не знаком, но думаю, что т.н. таски с этим справятся.
С виртуальными потоками нужно быть оооочень аккуратным, т.к они имеют очень высокую склонность к дедлокам на синхронизированных участках кода. Причем это учесть прям очень сложно с разгону, особенно если пихать в них какие-то сложные куски кода. Виртуальные потоки очень хороши там, где нужно ждать какое-то IO. Например, можно выгружать в отдельный виртуальный поток запись пакета в сетевой буфер.А как насчет виртуальных потоков в Яве? Я не пробовал, но из того что читал sleep должен переключить task на тот который можно будет делать. А потом все вроде переключится обратно.
/**
* <b>Описание:</b><br>
* <small>Запускает регенерацию жизненных, магических и боевых сил.</small>
*/
public synchronized void startRegeneration() {
if (!character.isDeading() && regenTask == null) {
if (currentCp < maxCp) {
regenType = RegenType.CP;
} else {
regenType = RegenType.HP_MP;
}
regenTask = Threads.getInstance().scheduleAtFixedRate(this::doRegeneration, 2500, 2500);
}
}
/**
* <b>Описание:</b><br>
* <small>Останавливает регенерацию жизненных, магических и боевых сил.</small>
*/
public synchronized void stopRegeneration() {
if (regenTask != null) {
regenTask.cancel(false);
regenTask = null;
}
}
/**
* <b>Описание:</b><br>
* <small>Запускается в такт, пока идет регенерация жизненных, магических и боевых сил.</small>
*/
private void doRegeneration() {
if (currentCp >= maxCp && currentHp >= maxHp && currentMp >= maxMp) {
stopRegeneration();
return;
}
final int idWorld = character.getIdWorld();
final double bonus = character.isSitting() ? 1.5 : 1;
switch (regenType) {
case CP -> {
if (currentCp < maxCp) {
final int newCurrentCp = (int) (currentCp + Math.max(1, cpRegen * bonus));
if (newCurrentCp >= maxCp) {
currentCp = maxCp;
} else {
currentCp = newCurrentCp;
}
final StatusUpdate statusUpdate = new StatusUpdate(idWorld);
statusUpdate.addUpdate(StatusType.CURRENT_CP, (int) currentCp);
character.sendPacket(statusUpdate);
}
if (currentHp < maxHp || currentMp < maxMp) regenType = RegenType.HP_MP;
}
case HP_MP -> {
if (currentHp < maxHp || currentMp < maxMp) {
if (currentHp < maxHp) {
final int newCurrentHp = (int) (currentHp + Math.max(1, hpRegen * bonus));
if (newCurrentHp >= maxHp) {
currentHp = maxHp;
} else {
currentHp = newCurrentHp;
}
}
if (currentMp < maxMp) {
final int newCurrentMp = (int) (currentMp + Math.max(1, mpRegen * bonus));
if (newCurrentMp >= maxMp) {
currentMp = maxMp;
} else {
currentMp = newCurrentMp;
}
}
final StatusUpdate statusUpdate = new StatusUpdate(idWorld);
statusUpdate.addUpdate(StatusType.CURRENT_HP, (int) currentHp);
statusUpdate.addUpdate(StatusType.CURRENT_MP, (int) currentMp);
character.sendPacket(statusUpdate);
}
if (currentCp < maxCp) regenType = RegenType.CP;
}
}
}
Какая там правильная механика регенерации? какой такт обновления ХП,МП,СП должен быть? я оптимизировал еще тот метод, внес флаги регенерации, засек выполнение метода при такте регенерации 0 мсек, меньше 1 миллисекунды ) вот только есть нюанс: или сделать равномерное время между тактами регенерации, что CP, что HP и МР но жертвуя двумя тактами вызова таска, или обойтись одним тактом, уменьшив нагрузку на железо в 2 раза и тогда получиться такая штука: если нужно обновлять скажем и СР и МР и НР, такт обновления будет равен для СР - 5 сек. и для НР и МР - 5 сек. так как такт задания 2,5 сек. я поставил, то есть 2,5 сек. прошло, обновило СР, потом 2,5 сек. прошло, обновило НР и МР, но если СР уже фул, а нужно обновлять НР и МР, тогда такт будет 2,5 сек. То есть если будет регенерация по отдельности, то регенерировать будет на 50% быстрее.
Это какая такая нагрузка на железо? У вас максимум две формулы с умножением и сложением. Ява это все оптимизирует очень хорошо, а вот структуры данных, нет. Поэтому я нe вижу ни нагрузки, ни оптимизации, ну и никакого смысла разделять регенерацию по двум логическим линиям.обойтись одним тактом, уменьшив нагрузку на железо в 2 раза
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?