World.java 8.68 KB
Newer Older
Melledy's avatar
Melledy committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package emu.grasscutter.game;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

import emu.grasscutter.game.entity.GenshinEntity;
import emu.grasscutter.game.props.ClimateType;
import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
Melledy's avatar
Melledy committed
17
18
import emu.grasscutter.data.GenshinData;
import emu.grasscutter.data.def.SceneData;
Melledy's avatar
Melledy committed
19
20
21
import emu.grasscutter.game.GenshinPlayer.SceneLoadState;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget;
22
import emu.grasscutter.game.entity.EntityGadget;
Melledy's avatar
Melledy committed
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
import emu.grasscutter.server.packet.send.PacketScenePlayerInfoNotify;
import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketWorldPlayerInfoNotify;
import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify;
38
import emu.grasscutter.utils.Position;
Melledy's avatar
Melledy committed
39
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
40
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
Melledy's avatar
Melledy committed
41
42
43
44
45
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;

public class World implements Iterable<GenshinPlayer> {
	private final GenshinPlayer owner;
	private final List<GenshinPlayer> players;
46
	private final Int2ObjectMap<GenshinScene> scenes;
Melledy's avatar
Melledy committed
47
48
49
50
51
	
	private int levelEntityId;
	private int nextEntityId = 0;
	private int nextPeerId = 0;
	private int worldLevel;
52

Melledy's avatar
Melledy committed
53
54
55
56
57
58
59
60
61
	private boolean isMultiplayer;
	
	public World(GenshinPlayer player) {
		this(player, false);
	}
	
	public World(GenshinPlayer player, boolean isMultiplayer) {
		this.owner = player;
		this.players = Collections.synchronizedList(new ArrayList<>());
62
		this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
63
		
Melledy's avatar
Melledy committed
64
65
66
		this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
		this.worldLevel = player.getWorldLevel();
		this.isMultiplayer = isMultiplayer;
67
68
		
		this.owner.getServer().registerWorld(this);
Melledy's avatar
Melledy committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
	}
	
	public GenshinPlayer getHost() {
		return owner;
	}

	public int getLevelEntityId() {
		return levelEntityId;
	}

	public int getHostPeerId() {
		if (this.getHost() == null) {
			return 0;
		}
		return this.getHost().getPeerId();
	}
	
	public int getNextPeerId() {
		return ++this.nextPeerId;
	}

	public int getWorldLevel() {
		return worldLevel;
	}

	public void setWorldLevel(int worldLevel) {
		this.worldLevel = worldLevel;
	}

	public List<GenshinPlayer> getPlayers() {
		return players;
	}
	
102
103
	public Int2ObjectMap<GenshinScene> getScenes() {
		return this.scenes;
Melledy's avatar
Melledy committed
104
105
	}
	
106
	public GenshinScene getSceneById(int sceneId) {
Melledy's avatar
Melledy committed
107
108
109
110
111
112
113
114
115
116
		// Get scene normally
		GenshinScene scene = getScenes().get(sceneId);
		if (scene != null) {
			return scene;
		}
		
		// Create scene from scene data if it doesnt exist
		SceneData sceneData = GenshinData.getSceneDataMap().get(sceneId);
		if (sceneData != null) {
			scene = new GenshinScene(this, sceneData);
117
			this.registerScene(scene);
Melledy's avatar
Melledy committed
118
119
120
121
			return scene;
		}
		
		return null;
Melledy's avatar
Melledy committed
122
123
	}
	
124
125
	public int getPlayerCount() {
		return getPlayers().size();
Melledy's avatar
Melledy committed
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
	}
	
	public boolean isMultiplayer() {
		return isMultiplayer;
	}
	
	public int getNextEntityId(EntityIdType idType) {
		return (idType.getId() << 24) + ++this.nextEntityId;
	}
	
	public synchronized void addPlayer(GenshinPlayer player) {
		// Check if player already in
		if (getPlayers().contains(player)) {
			return;
		}
		
		// Remove player from prev world
		if (player.getWorld() != null) {
			player.getWorld().removePlayer(player);
		}
		
		// Register
		player.setWorld(this);
		getPlayers().add(player);
150
151

		// Set player variables
Melledy's avatar
Melledy committed
152
153
154
		player.setPeerId(this.getNextPeerId());
		player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
		
155
156
157
		// Copy main team to mp team
		if (this.isMultiplayer()) {
			player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize());
158
			player.getTeamManager().setCurrentCharacterIndex(0);
159
		}
Melledy's avatar
Melledy committed
160
		
161
162
163
164
		// Add to scene
		GenshinScene scene = this.getSceneById(player.getSceneId());
		scene.addPlayer(player);

Melledy's avatar
Melledy committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
		// Info packet for other players
		if (this.getPlayers().size() > 1) {
			this.updatePlayerInfos(player);
		}
	}

	public synchronized void removePlayer(GenshinPlayer player) {
		// Remove team entities
		player.sendPacket(
				new PacketDelTeamEntityNotify(
						player.getSceneId(), 
						getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
				)
		);
		
		// Deregister
		getPlayers().remove(player);
		player.setWorld(null);
		
184
185
186
187
		// Remove from scene
		GenshinScene scene = this.getSceneById(player.getSceneId());
		scene.removePlayer(player);

Melledy's avatar
Melledy committed
188
189
190
191
		// Info packet for other players
		if (this.getPlayers().size() > 0) {
			this.updatePlayerInfos(player);
		}
192

Melledy's avatar
Melledy committed
193
194
195
196
197
198
199
		// Disband world if host leaves
		if (getHost() == player) {
			List<GenshinPlayer> kicked = new ArrayList<>(this.getPlayers());
			for (GenshinPlayer victim : kicked) {
				World world = new World(victim);
				world.addPlayer(victim);
				
200
				victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.EnterSelf, EnterReason.TeamKick, victim.getSceneId(), victim.getPos()));
Melledy's avatar
Melledy committed
201
202
203
204
			}
		}
	}
	
205
206
207
208
209
210
211
212
	public void registerScene(GenshinScene scene) {
		this.getScenes().put(scene.getId(), scene);
	}
	
	public void deregisterScene(GenshinScene scene) {
		this.getScenes().remove(scene.getId());
	}
	
Melledy's avatar
Melledy committed
213
	public boolean transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) {
214
		if (GenshinData.getSceneDataMap().get(sceneId) == null) {
Melledy's avatar
Melledy committed
215
			return false;
216
217
		}
		
218
		GenshinScene oldScene = null;
Yazawazi's avatar
Yazawazi committed
219

220
		if (player.getScene() != null) {
221
222
223
224
225
226
227
228
			oldScene = player.getScene();
			
			// Dont deregister scenes if the player is going to tp back into them
			if (oldScene.getId() == sceneId) {
				oldScene.setDontDestroyWhenEmpty(true);
			}
			
			oldScene.removePlayer(player);
229
230
		}
		
231
232
		GenshinScene newScene = this.getSceneById(sceneId);
		newScene.addPlayer(player);
233
		player.getPos().set(pos);
Melledy's avatar
Melledy committed
234
		
235
236
237
238
		if (oldScene != null) {
			oldScene.setDontDestroyWhenEmpty(false);
		}
		
Melledy's avatar
Melledy committed
239
		// Teleport packet
240
		if (oldScene == newScene) {
Yazawazi's avatar
Yazawazi committed
241
242
243
244
			player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterGoto, EnterReason.TransPoint, sceneId, pos));
		} else {
			player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterJump, EnterReason.TransPoint, sceneId, pos));
		}
Melledy's avatar
Melledy committed
245
		return true;
246
247
	}
	
Melledy's avatar
Melledy committed
248
249
	private void updatePlayerInfos(GenshinPlayer paramPlayer) {
		for (GenshinPlayer player : getPlayers()) {
250
			// Dont send packets if player is loading in and filter out joining player
Melledy's avatar
Melledy committed
251
252
253
254
			if (!player.hasSentAvatarDataNotify() || player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() || player == paramPlayer) {
				continue;
			}
			
255
256
257
258
259
260
			// Update team of all players since max players has been changed - Probably not the best way to do it
			if (this.isMultiplayer()) {
				player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
				player.getTeamManager().updateTeamEntities(null);
			}

Melledy's avatar
Melledy committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
			// World player info packets
			player.getSession().send(new PacketWorldPlayerInfoNotify(this));
			player.getSession().send(new PacketScenePlayerInfoNotify(this));
			player.getSession().send(new PacketWorldPlayerRTTNotify(this));
			
			// Team packets
			player.getSession().send(new PacketSyncTeamEntityNotify(player));
			player.getSession().send(new PacketSyncScenePlayTeamEntityNotify(player));
		}
	}
	
	public void broadcastPacket(GenshinPacket packet) {
    	// Send to all players - might have to check if player has been sent data packets
    	for (GenshinPlayer player : this.getPlayers()) {
    		player.getSession().send(packet);
    	}
	}
	
279
280
281
282
283
284
	public void onTick() {
		for (GenshinScene scene : this.getScenes().values()) {
			scene.onTick();
		}
	}
	
Melledy's avatar
Melledy committed
285
286
287
288
289
290
291
292
293
	public void close() {
		
	}

	@Override
	public Iterator<GenshinPlayer> iterator() {
		return getPlayers().iterator();
	}
}