fix: NPE related to teapot when player logs in. (#2429)

* fix: NPE related to home when player logs in.

* fix: NPE related to home when player logs in.

* forgot to save player after fixing module id
This commit is contained in:
hamusuke 2023-11-10 11:56:21 +09:00 committed by GitHub
parent 2bcbd41026
commit f1c1a84683
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 274 additions and 154 deletions

View File

@ -302,6 +302,10 @@ public final class GameData {
private static final Int2ObjectMap<HomeWorldLevelData> homeWorldLevelDataMap = private static final Int2ObjectMap<HomeWorldLevelData> homeWorldLevelDataMap =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
@Getter
private static final Int2ObjectMap<HomeWorldModuleData> homeWorldModuleDataMap =
new Int2ObjectOpenHashMap<>();
@Getter @Getter
private static final Int2ObjectMap<HomeWorldNPCData> homeWorldNPCDataMap = private static final Int2ObjectMap<HomeWorldNPCData> homeWorldNPCDataMap =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();

View File

@ -0,0 +1,17 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "HomeworldModuleExcelConfigData.json")
@FieldDefaults(level = AccessLevel.PRIVATE)
@Getter
public class HomeWorldModuleData extends GameResource {
int Id;
boolean isFree;
int worldSceneId;
int defaultRoomSceneId;
}

View File

@ -9,19 +9,21 @@ import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType; import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.net.proto.HomeAvatarTalkFinishInfoOuterClass; import emu.grasscutter.net.proto.HomeAvatarTalkFinishInfoOuterClass.HomeAvatarTalkFinishInfo;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntSets;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Entity(value = "homes", useDiscriminator = false) @Entity(value = "homes", useDiscriminator = false)
@Data @Data
@ -36,6 +38,9 @@ public class GameHome {
|| sceneData.getSceneType() == SceneType.SCENE_HOME_ROOM) || sceneData.getSceneType() == SceneType.SCENE_HOME_ROOM)
.map(SceneData::getId) .map(SceneData::getId)
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
public static final Set<Integer> HOME_MODULE_IDS =
GameData.getHomeWorldModuleDataMap().isEmpty() ?
IntSets.fromTo(1, 6) : GameData.getHomeWorldModuleDataMap().keySet();
@Id String id; @Id String id;
@ -181,6 +186,7 @@ public class GameHome {
public void onOwnerLogin(Player player) { public void onOwnerLogin(Player player) {
this.player = player; // update player pointer. (prevent offline player from sending packet) this.player = player; // update player pointer. (prevent offline player from sending packet)
this.fixModuleIdIfInvalid();
player.getSession().send(new PacketHomeBasicInfoNotify(player, false)); player.getSession().send(new PacketHomeBasicInfoNotify(player, false));
player.getSession().send(new PacketPlayerHomeCompInfoNotify(player)); player.getSession().send(new PacketPlayerHomeCompInfoNotify(player));
player.getSession().send(new PacketHomeComfortInfoNotify(player)); player.getSession().send(new PacketHomeComfortInfoNotify(player));
@ -194,6 +200,29 @@ public class GameHome {
player.getSession().send(new PacketHomeResourceNotify(player)); player.getSession().send(new PacketHomeResourceNotify(player));
} }
private void fixModuleIdIfInvalid() {
if (this.player.hasSentLoginPackets() || this.player.getRealmList() == null) {
return;
}
this.player.getRealmList().removeIf(integer -> !HOME_MODULE_IDS.contains(integer)); // Delete invalid module ids.
if (this.player.getRealmList().isEmpty()) {
this.player.setRealmList(null);
this.player.save();
return;
}
if (this.player.getCurrentRealmId() <= 0 || !this.player.getCurHomeWorld().isRealmIdValid()) {
int firstRId = this.player.getRealmList().iterator().next();
this.player.setCurrentRealmId(firstRId);
this.player.save();
Grasscutter.getLogger().info("Set player {}'s current realm id to {} cuz the id is invalid.", this.player.getUid(), firstRId);
}
this.player.getCurHomeWorld().refreshModuleManager(); // Apply module id fix.
}
public void onPlayerChangedAvatarCostume(Avatar avatar) { public void onPlayerChangedAvatarCostume(Avatar avatar) {
var world = this.player.getServer().getHomeWorldOrCreate(this.player); var world = this.player.getServer().getHomeWorldOrCreate(this.player);
world.broadcastPacket( world.broadcastPacket(
@ -239,7 +268,7 @@ public class GameHome {
return this.finishedTalkIdMap.get(avatarId); return this.finishedTalkIdMap.get(avatarId);
} }
public List<HomeAvatarTalkFinishInfoOuterClass.HomeAvatarTalkFinishInfo> public List<HomeAvatarTalkFinishInfo>
toAvatarTalkFinishInfoProto() { toAvatarTalkFinishInfoProto() {
if (this.finishedTalkIdMap == null) { if (this.finishedTalkIdMap == null) {
this.finishedTalkIdMap = new HashMap<>(); this.finishedTalkIdMap = new HashMap<>();
@ -248,7 +277,7 @@ public class GameHome {
return this.finishedTalkIdMap.entrySet().stream() return this.finishedTalkIdMap.entrySet().stream()
.map( .map(
e -> { e -> {
return HomeAvatarTalkFinishInfoOuterClass.HomeAvatarTalkFinishInfo.newBuilder() return HomeAvatarTalkFinishInfo.newBuilder()
.setAvatarId(e.getKey()) .setAvatarId(e.getKey())
.addAllFinishTalkIdList(e.getValue()) .addAllFinishTalkIdList(e.getValue())
.build(); .build();

View File

@ -1,22 +1,25 @@
package emu.grasscutter.game.home; package emu.grasscutter.game.home;
import com.github.davidmoten.guavamini.Lists; import com.github.davidmoten.guavamini.Lists;
import emu.grasscutter.game.home.suite.HomeSuiteItem;
import emu.grasscutter.game.home.suite.event.HomeAvatarRewardEvent; import emu.grasscutter.game.home.suite.event.HomeAvatarRewardEvent;
import emu.grasscutter.game.home.suite.event.HomeAvatarSummonEvent; import emu.grasscutter.game.home.suite.event.HomeAvatarSummonEvent;
import emu.grasscutter.game.home.suite.event.SuiteEventType; import emu.grasscutter.game.home.suite.event.SuiteEventType;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.HomeAvatarRewardEventNotifyOuterClass; import emu.grasscutter.net.proto.HomeAvatarRewardEventNotifyOuterClass.HomeAvatarRewardEventNotify;
import emu.grasscutter.net.proto.HomeAvatarSummonAllEventNotifyOuterClass; import emu.grasscutter.net.proto.HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify;
import emu.grasscutter.net.proto.RetcodeOuterClass; import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonAllEventNotify; import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonAllEventNotify;
import emu.grasscutter.utils.Either; import emu.grasscutter.utils.Either;
import java.util.*;
import java.util.stream.Stream;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Stream;
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeModuleManager { public class HomeModuleManager {
@ -24,7 +27,9 @@ public class HomeModuleManager {
final HomeWorld homeWorld; final HomeWorld homeWorld;
final GameHome home; final GameHome home;
final int moduleId; final int moduleId;
@Nullable
final HomeScene outdoor; final HomeScene outdoor;
@Nullable
HomeScene indoor; HomeScene indoor;
final List<HomeAvatarRewardEvent> rewardEvents; final List<HomeAvatarRewardEvent> rewardEvents;
final List<HomeAvatarSummonEvent> summonEvents; final List<HomeAvatarSummonEvent> summonEvents;
@ -45,8 +50,14 @@ public class HomeModuleManager {
return; return;
} }
this.outdoor.onTick(); if (this.outdoor != null) {
this.indoor.onTick(); this.outdoor.onTick();
}
if (this.indoor != null) {
this.indoor.onTick();
}
this.summonEvents.removeIf(HomeAvatarSummonEvent::isTimeOver); this.summonEvents.removeIf(HomeAvatarSummonEvent::isTimeOver);
} }
@ -66,44 +77,45 @@ public class HomeModuleManager {
private void fireAllAvatarRewardEvents() { private void fireAllAvatarRewardEvents() {
this.rewardEvents.clear(); this.rewardEvents.clear();
var allBlockItems = var allBlockItems =
Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem()) Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem())
.map(HomeSceneItem::getBlockItems) .filter(Objects::nonNull)
.map(Map::values) .map(HomeSceneItem::getBlockItems)
.flatMap(Collection::stream) .map(Map::values)
.toList(); .flatMap(Collection::stream)
.toList();
var suites = var suites =
allBlockItems.stream() allBlockItems.stream()
.map(HomeBlockItem::getSuiteList) .map(HomeBlockItem::getSuiteList)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.distinct() .distinct()
.toList(); .toList();
allBlockItems.stream() allBlockItems.stream()
.map(HomeBlockItem::getDeployNPCList) .map(HomeBlockItem::getDeployNPCList)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.forEach( .forEach(
avatar -> { avatar -> {
suites.forEach( suites.forEach(
suite -> { suite -> {
var data = var data =
SuiteEventType.HOME_AVATAR_REWARD_EVENT.getEventDataFrom( SuiteEventType.HOME_AVATAR_REWARD_EVENT.getEventDataFrom(
avatar.getAvatarId(), suite.getSuiteId()); avatar.getAvatarId(), suite.getSuiteId());
if (data == null || this.home.isRewardEventFinished(data.getId())) { if (data == null || this.home.isRewardEventFinished(data.getId())) {
return; return;
} }
this.rewardEvents.add( this.rewardEvents.add(
new HomeAvatarRewardEvent( new HomeAvatarRewardEvent(
homeOwner, homeOwner,
data.getId(), data.getId(),
data.getRewardID(), data.getRewardID(),
data.getAvatarID(), data.getAvatarID(),
data.getSuiteId(), data.getSuiteId(),
suite.getGuid())); suite.getGuid()));
});
}); });
});
if (this.summonEvents != null) { if (this.summonEvents != null) {
var suiteIdList = this.rewardEvents.stream().map(HomeAvatarRewardEvent::getSuiteId).toList(); var suiteIdList = this.rewardEvents.stream().map(HomeAvatarRewardEvent::getSuiteId).toList();
@ -113,68 +125,71 @@ public class HomeModuleManager {
private void cancelSummonEventsIfAvatarLeave() { private void cancelSummonEventsIfAvatarLeave() {
var avatars = var avatars =
Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem()) Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem())
.map(HomeSceneItem::getBlockItems) .filter(Objects::nonNull)
.map(Map::values) .map(HomeSceneItem::getBlockItems)
.flatMap(Collection::stream) .map(Map::values)
.map(HomeBlockItem::getDeployNPCList) .flatMap(Collection::stream)
.flatMap(Collection::stream) .map(HomeBlockItem::getDeployNPCList)
.map(HomeNPCItem::getAvatarId) .flatMap(Collection::stream)
.toList(); .map(HomeNPCItem::getAvatarId)
.toList();
this.summonEvents.removeIf(event -> !avatars.contains(event.getAvatarId())); this.summonEvents.removeIf(event -> !avatars.contains(event.getAvatarId()));
} }
public Either<List<GameItem>, Integer> claimAvatarRewards(int eventId) { public Either<List<GameItem>, Integer> claimAvatarRewards(int eventId) {
if (this.rewardEvents.isEmpty()) { if (this.rewardEvents.isEmpty()) {
return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE); return Either.right(Retcode.RET_FAIL_VALUE);
} }
var event = this.rewardEvents.remove(0); var event = this.rewardEvents.remove(0);
if (event.getEventId() != eventId) { if (event.getEventId() != eventId) {
return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE); return Either.right(Retcode.RET_FAIL_VALUE);
} }
if (!this.homeOwner.getHome().onClaimAvatarRewards(eventId)) { if (!this.homeOwner.getHome().onClaimAvatarRewards(eventId)) {
return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE); return Either.right(Retcode.RET_FAIL_VALUE);
} }
return Either.left(event.giveRewards()); return Either.left(event.giveRewards());
} }
public Either<HomeAvatarSummonEvent, Integer> fireAvatarSummonEvent( public Either<HomeAvatarSummonEvent, Integer> fireAvatarSummonEvent(
Player owner, int avatarId, int guid, int suiteId) { Player owner, int avatarId, int guid, int suiteId) {
var targetSuite = HomeSuiteItem targetSuite = null;
((HomeScene) owner.getScene()) if (owner.getScene() instanceof HomeScene homeScene) {
.getSceneItem().getBlockItems().values().stream() targetSuite = homeScene
.map(HomeBlockItem::getSuiteList) .getSceneItem().getBlockItems().values().stream()
.flatMap(Collection::stream) .map(HomeBlockItem::getSuiteList)
.filter(suite -> suite.getGuid() == guid) .flatMap(Collection::stream)
.findFirst() .filter(suite -> suite.getGuid() == guid)
.orElse(null); .findFirst()
.orElse(null);
}
if (this.isInRewardEvent(avatarId)) { if (this.isInRewardEvent(avatarId)) {
return Either.right(RetcodeOuterClass.Retcode.RET_DUPLICATE_AVATAR_VALUE); return Either.right(Retcode.RET_DUPLICATE_AVATAR_VALUE);
} }
if (this.rewardEvents.stream().anyMatch(event -> event.getGuid() == guid)) { if (this.rewardEvents.stream().anyMatch(event -> event.getGuid() == guid)) {
return Either.right(RetcodeOuterClass.Retcode.RET_HOME_FURNITURE_GUID_ERROR_VALUE); return Either.right(Retcode.RET_HOME_FURNITURE_GUID_ERROR_VALUE);
} }
this.summonEvents.removeIf(event -> event.getGuid() == guid || event.getAvatarId() == avatarId); this.summonEvents.removeIf(event -> event.getGuid() == guid || event.getAvatarId() == avatarId);
if (targetSuite == null) { if (targetSuite == null) {
return Either.right(RetcodeOuterClass.Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE); return Either.right(Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE);
} }
var eventData = SuiteEventType.HOME_AVATAR_SUMMON_EVENT.getEventDataFrom(avatarId, suiteId); var eventData = SuiteEventType.HOME_AVATAR_SUMMON_EVENT.getEventDataFrom(avatarId, suiteId);
if (eventData == null) { if (eventData == null) {
return Either.right(RetcodeOuterClass.Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE); return Either.right(Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE);
} }
var event = var event =
new HomeAvatarSummonEvent( new HomeAvatarSummonEvent(
owner, eventData.getId(), eventData.getRewardID(), avatarId, suiteId, guid); owner, eventData.getId(), eventData.getRewardID(), avatarId, suiteId, guid);
this.summonEvents.add(event); this.summonEvents.add(event);
owner.sendPacket(new PacketHomeAvatarSummonAllEventNotify(owner)); owner.sendPacket(new PacketHomeAvatarSummonAllEventNotify(owner));
return Either.left(event); return Either.left(event);
@ -184,38 +199,39 @@ public class HomeModuleManager {
this.summonEvents.removeIf(event -> event.getEventId() == eventId); this.summonEvents.removeIf(event -> event.getEventId() == eventId);
} }
public HomeAvatarRewardEventNotifyOuterClass.HomeAvatarRewardEventNotify toRewardEventProto() { public HomeAvatarRewardEventNotify toRewardEventProto() {
var notify = HomeAvatarRewardEventNotifyOuterClass.HomeAvatarRewardEventNotify.newBuilder(); var notify = HomeAvatarRewardEventNotify.newBuilder();
if (!this.rewardEvents.isEmpty()) { if (!this.rewardEvents.isEmpty()) {
notify.setRewardEvent(this.rewardEvents.get(0).toProto()).setIsEventTrigger(true); notify.setRewardEvent(this.rewardEvents.get(0).toProto()).setIsEventTrigger(true);
notify.addAllPendingList( notify.addAllPendingList(
this.rewardEvents.subList(1, this.rewardEvents.size()).stream() this.rewardEvents.subList(1, this.rewardEvents.size()).stream()
.map(HomeAvatarRewardEvent::toProto) .map(HomeAvatarRewardEvent::toProto)
.toList()); .toList());
} }
return notify.build(); return notify.build();
} }
public HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify public HomeAvatarSummonAllEventNotify toSummonEventProto() {
toSummonEventProto() { return HomeAvatarSummonAllEventNotify.newBuilder()
return HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify.newBuilder() .addAllSummonEventList(
.addAllSummonEventList( this.summonEvents.stream().map(HomeAvatarSummonEvent::toProto).toList())
this.summonEvents.stream().map(HomeAvatarSummonEvent::toProto).toList()) .build();
.build();
} }
public boolean isInRewardEvent(int avatarId) { public boolean isInRewardEvent(int avatarId) {
return this.rewardEvents.stream().anyMatch(e -> e.getAvatarId() == avatarId); return this.rewardEvents.stream().anyMatch(e -> e.getAvatarId() == avatarId);
} }
@Nullable
public HomeSceneItem getOutdoorSceneItem() { public HomeSceneItem getOutdoorSceneItem() {
return this.outdoor.getSceneItem(); return this.outdoor == null ? null : this.outdoor.getSceneItem();
} }
@Nullable
public HomeSceneItem getIndoorSceneItem() { public HomeSceneItem getIndoorSceneItem() {
return this.indoor.getSceneItem(); return this.indoor == null ? null : this.indoor.getSceneItem();
} }
public void onSetModule() { public void onSetModule() {
@ -223,8 +239,14 @@ public class HomeModuleManager {
return; return;
} }
this.outdoor.addEntities(this.getOutdoorSceneItem().getAnimals(this.outdoor)); if (this.outdoor != null) {
this.indoor.addEntities(this.getIndoorSceneItem().getAnimals(this.indoor)); this.outdoor.addEntities(this.getOutdoorSceneItem().getAnimals(this.outdoor));
}
if (this.indoor != null) {
this.indoor.addEntities(this.getIndoorSceneItem().getAnimals(this.indoor));
}
this.fireAllAvatarRewardEvents(); this.fireAllAvatarRewardEvents();
} }
@ -233,7 +255,12 @@ public class HomeModuleManager {
return; return;
} }
this.outdoor.getEntities().clear(); if (this.outdoor != null) {
this.indoor.getEntities().clear(); this.outdoor.getEntities().clear();
}
if (this.indoor != null) {
this.indoor.getEntities().clear();
}
} }
} }

View File

@ -3,7 +3,6 @@ package emu.grasscutter.game.home;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.game.entity.EntityTeam; import emu.grasscutter.game.entity.EntityTeam;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.World;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.ChatInfoOuterClass; import emu.grasscutter.net.proto.ChatInfoOuterClass;
@ -11,9 +10,11 @@ import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketPlayerChatNotify; import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify; import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify;
import lombok.Getter;
import javax.annotation.Nullable;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import lombok.Getter;
@Getter @Getter
public class HomeWorld extends World { public class HomeWorld extends World {
@ -66,7 +67,7 @@ public class HomeWorld extends World {
} }
public boolean isRealmIdValid() { public boolean isRealmIdValid() {
return this.getHost().getCurrentRealmId() > 0; return this.getSceneById(this.getHost().getCurrentRealmId() + 2000) != null;
} }
@Override @Override
@ -147,11 +148,13 @@ public class HomeWorld extends World {
player.setWorld(null); player.setWorld(null);
// Remove from scene // Remove from scene
Scene scene = this.getSceneById(player.getSceneId()); var scene = this.getSceneById(player.getSceneId());
scene.removePlayer(player); if (scene != null) {
scene.removePlayer(player);
}
// Info packet for other players // Info packet for other players
if (this.getPlayers().size() > 0) { if (!this.getPlayers().isEmpty()) {
this.updatePlayerInfos(player); this.updatePlayerInfos(player);
} }
@ -167,6 +170,7 @@ public class HomeWorld extends World {
} }
@Override @Override
@Nullable
public HomeScene getSceneById(int sceneId) { public HomeScene getSceneById(int sceneId) {
var scene = this.getScenes().get(sceneId); var scene = this.getScenes().get(sceneId);
if (scene instanceof HomeScene homeScene) { if (scene instanceof HomeScene homeScene) {

View File

@ -139,11 +139,18 @@ public class HomeWorldMPSystem extends BaseGameSystem {
int realmId = 2000 + owner.getCurrentRealmId(); int realmId = 2000 + owner.getCurrentRealmId();
var item = targetHome.getHomeSceneItem(realmId); var item = targetHome.getHomeSceneItem(realmId);
var scene = world.getSceneById(realmId);
targetHome.save(); targetHome.save();
var pos =
Position pos;
if (scene != null) {
pos =
toSafe toSafe
? world.getSceneById(realmId).getScriptManager().getConfig().born_pos ? scene.getScriptManager().getConfig().born_pos
: item.getBornPos(); : item.getBornPos();
} else {
pos = item.getBornPos();
}
if (teleportPoint != 0) { if (teleportPoint != 0) {
var target = item.getTeleportPointPos(teleportPoint); var target = item.getTeleportPointPos(teleportPoint);

View File

@ -1,18 +1,21 @@
package emu.grasscutter.game.world; package emu.grasscutter.game.world;
import static emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType.SCRIPT;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.dungeon.DungeonData; import emu.grasscutter.data.excels.dungeon.DungeonData;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.EntityTeam;
import emu.grasscutter.game.entity.EntityWorld;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.Player.SceneLoadState; import emu.grasscutter.game.player.Player.SceneLoadState;
import emu.grasscutter.game.props.*; import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.quest.enums.QuestContent; import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.data.TeleportProperties; import emu.grasscutter.game.world.data.TeleportProperties;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo.*; import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo.SystemHint;
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo.SystemHintType;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.scripts.data.SceneConfig; import emu.grasscutter.scripts.data.SceneConfig;
import emu.grasscutter.server.event.player.PlayerTeleportEvent; import emu.grasscutter.server.event.player.PlayerTeleportEvent;
@ -21,12 +24,25 @@ import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.ConversionUtils; import emu.grasscutter.utils.ConversionUtils;
import io.netty.util.concurrent.FastThreadLocalThread; import io.netty.util.concurrent.FastThreadLocalThread;
import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.*; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import java.util.concurrent.*; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.*; import lombok.Getter;
import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType.SCRIPT;
public class World implements Iterable<Player> { public class World implements Iterable<Player> {
@Getter private final GameServer server; @Getter private final GameServer server;
@Getter private Player host; @Getter private Player host;
@ -124,6 +140,7 @@ public class World implements Iterable<Player> {
* @param sceneId The scene ID. * @param sceneId The scene ID.
* @return The scene. * @return The scene.
*/ */
@Nullable
public Scene getSceneById(int sceneId) { public Scene getSceneById(int sceneId) {
// Get scene normally // Get scene normally
var scene = this.getScenes().get(sceneId); var scene = this.getScenes().get(sceneId);

View File

@ -1,9 +1,11 @@
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.HomeChangeModuleReqOuterClass; import emu.grasscutter.net.proto.HomeChangeModuleReqOuterClass.HomeChangeModuleReq;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketHomeAvatarTalkFinishInfoNotify; import emu.grasscutter.server.packet.send.PacketHomeAvatarTalkFinishInfoNotify;
@ -16,12 +18,20 @@ public class HandlerHomeChangeModuleReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
HomeChangeModuleReqOuterClass.HomeChangeModuleReq req = var req = HomeChangeModuleReq.parseFrom(payload);
HomeChangeModuleReqOuterClass.HomeChangeModuleReq.parseFrom(payload);
var homeWorld = session.getPlayer().getCurHomeWorld(); var homeWorld = session.getPlayer().getCurHomeWorld();
if (!homeWorld.getGuests().isEmpty()) { if (!homeWorld.getGuests().isEmpty()) {
session.send(new PacketHomeChangeModuleRsp()); session.send(new PacketHomeChangeModuleRsp(Retcode.RET_HOME_HAS_GUEST));
return;
}
int realmId = 2000 + req.getTargetModuleId();
var scene = homeWorld.getSceneById(realmId);
if (scene == null) {
Grasscutter.getLogger().warn("scene == null! Changing module will fail.");
session.send(new PacketHomeChangeModuleRsp(Retcode.RET_INVALID_SCENE_ID));
return; return;
} }
@ -31,8 +41,6 @@ public class HandlerHomeChangeModuleReq extends PacketHandler {
session.send(new PacketPlayerHomeCompInfoNotify(session.getPlayer())); session.send(new PacketPlayerHomeCompInfoNotify(session.getPlayer()));
session.send(new PacketHomeComfortInfoNotify(session.getPlayer())); session.send(new PacketHomeComfortInfoNotify(session.getPlayer()));
int realmId = 2000 + req.getTargetModuleId();
var scene = homeWorld.getSceneById(realmId);
var pos = scene.getScriptManager().getConfig().born_pos; var pos = scene.getScriptManager().getConfig().born_pos;
homeWorld.transferPlayerToScene(session.getPlayer(), realmId, TeleportType.WAYPOINT, pos); homeWorld.transferPlayerToScene(session.getPlayer(), realmId, TeleportType.WAYPOINT, pos);

View File

@ -35,7 +35,7 @@ public class HandlerHomeSceneJumpReq extends PacketHandler {
} }
world.transferPlayerToScene( world.transferPlayerToScene(
session.getPlayer(), req.getIsEnterRoomScene() ? homeScene.getRoomSceneId() : realmId, pos); session.getPlayer(), scene.getId(), pos);
session.send(new PacketHomeSceneJumpRsp(req.getIsEnterRoomScene())); session.send(new PacketHomeSceneJumpRsp(req.getIsEnterRoomScene()));
} }

View File

@ -2,28 +2,26 @@ package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.HomeChangeModuleRspOuterClass; import emu.grasscutter.net.proto.HomeChangeModuleRspOuterClass.HomeChangeModuleRsp;
import emu.grasscutter.net.proto.RetcodeOuterClass; import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
public class PacketHomeChangeModuleRsp extends BasePacket { public class PacketHomeChangeModuleRsp extends BasePacket {
public PacketHomeChangeModuleRsp(int targetModuleId) { public PacketHomeChangeModuleRsp(int targetModuleId) {
super(PacketOpcodes.HomeChangeModuleRsp); super(PacketOpcodes.HomeChangeModuleRsp);
HomeChangeModuleRspOuterClass.HomeChangeModuleRsp proto = var proto = HomeChangeModuleRsp.newBuilder()
HomeChangeModuleRspOuterClass.HomeChangeModuleRsp.newBuilder() .setRetcode(0)
.setRetcode(0) .setTargetModuleId(targetModuleId)
.setTargetModuleId(targetModuleId) .build();
.build();
this.setData(proto); this.setData(proto);
} }
public PacketHomeChangeModuleRsp() { public PacketHomeChangeModuleRsp(Retcode retcode) {
super(PacketOpcodes.HomeChangeModuleRsp); super(PacketOpcodes.HomeChangeModuleRsp);
this.setData( this.setData(HomeChangeModuleRsp.newBuilder()
HomeChangeModuleRspOuterClass.HomeChangeModuleRsp.newBuilder() .setRetcode(retcode.getNumber()));
.setRetcode(RetcodeOuterClass.Retcode.RET_HOME_HAS_GUEST_VALUE));
} }
} }

View File

@ -1,10 +1,14 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.home.HomeBlockItem; import emu.grasscutter.game.home.HomeBlockItem;
import emu.grasscutter.game.home.HomeMarkPointProtoFactory; import emu.grasscutter.game.home.HomeMarkPointProtoFactory;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.*; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.*; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.HomeMarkPointNotifyOuterClass.HomeMarkPointNotify;
import emu.grasscutter.net.proto.HomeMarkPointSceneDataOuterClass.HomeMarkPointSceneData;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
@ -13,49 +17,54 @@ public class PacketHomeMarkPointNotify extends BasePacket {
public PacketHomeMarkPointNotify(Player player) { public PacketHomeMarkPointNotify(Player player) {
super(PacketOpcodes.HomeMarkPointNotify); super(PacketOpcodes.HomeMarkPointNotify);
var proto = HomeMarkPointNotifyOuterClass.HomeMarkPointNotify.newBuilder(); var proto = HomeMarkPointNotify.newBuilder();
var world = player.getCurHomeWorld(); var world = player.getCurHomeWorld();
var owner = world.getHost(); var owner = world.getHost();
var home = world.getHome(); var home = world.getHome();
if (owner.getRealmList() == null) { if (owner.getRealmList() == null || owner.getRealmList().isEmpty()) {
return; return;
} }
// send current home mark points. // send current home mark points.
var moduleId = owner.getCurrentRealmId(); var moduleId = owner.getCurrentRealmId();
var scene = world.getSceneById(moduleId + 2000);
if (scene == null) {
Grasscutter.getLogger().warn("Current Realm id is invalid! SceneExcelConfigData.json, game resource not loaded correctly or the realm id maybe wrong?!");
return;
}
var homeScene = home.getHomeSceneItem(moduleId + 2000); var homeScene = home.getHomeSceneItem(moduleId + 2000);
var mainHouse = home.getMainHouseItem(moduleId + 2000); var mainHouse = home.getMainHouseItem(moduleId + 2000);
Set.of(homeScene, mainHouse) Set.of(homeScene, mainHouse)
.forEach( .forEach(
homeSceneItem -> { homeSceneItem -> {
var markPointData = var markPointData =
HomeMarkPointSceneDataOuterClass.HomeMarkPointSceneData.newBuilder() HomeMarkPointSceneData.newBuilder()
.setModuleId(moduleId) .setModuleId(moduleId)
.setSceneId(homeSceneItem.getSceneId()); .setSceneId(homeSceneItem.getSceneId());
if (!homeSceneItem.isRoom()) { if (!homeSceneItem.isRoom()) {
var config = world.getSceneById(moduleId + 2000).getScriptManager().getConfig(); var config = scene.getScriptManager().getConfig();
markPointData markPointData
.setSafePointPos( .setSafePointPos(
config == null config == null
? homeSceneItem.getBornPos().toProto() ? homeSceneItem.getBornPos().toProto()
: config.born_pos.toProto()) : config.born_pos.toProto())
.setTeapotSpiritPos(homeSceneItem.getDjinnPos().toProto()); .setTeapotSpiritPos(homeSceneItem.getDjinnPos().toProto());
} }
var marks = var marks =
homeSceneItem.getBlockItems().values().stream() homeSceneItem.getBlockItems().values().stream()
.map(HomeBlockItem::getMarkPointProtoFactories) .map(HomeBlockItem::getMarkPointProtoFactories)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.filter(HomeMarkPointProtoFactory::isProtoConvertible) .filter(HomeMarkPointProtoFactory::isProtoConvertible)
.map(HomeMarkPointProtoFactory::toMarkPointProto) .map(HomeMarkPointProtoFactory::toMarkPointProto)
.toList(); .toList();
markPointData.addAllFurnitureList(marks); markPointData.addAllFurnitureList(marks);
proto.addMarkPointDataList(markPointData); proto.addMarkPointDataList(markPointData);
}); });
this.setData(proto); this.setData(proto);
} }