de.pewpewproject.lasertag.lasertaggame.state.management.server.implementation.SpawnpointManager   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 120
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 52
dl 0
loc 120
rs 10
c 0
b 0
f 0
wmc 15

6 Methods

Rating   Name   Duplication   Size   Complexity  
B initSpawnpointCache(ServerWorld) 0 68 8
A initSpawnpointCacheIfNecessary(ServerWorld,boolean) 0 6 3
A clearSpawnpointCache() 0 3 1
A SpawnpointManager(ITeamsConfigState) 0 2 1
A getAllSpawnpoints() 0 6 1
A getSpawnpoints(TeamDto) 0 3 1
1
package de.pewpewproject.lasertag.lasertaggame.state.management.server.implementation;
2
3
import de.pewpewproject.lasertag.LasertagMod;
4
import de.pewpewproject.lasertag.lasertaggame.state.management.server.ISpawnpointManager;
5
import de.pewpewproject.lasertag.lasertaggame.state.synced.ITeamsConfigState;
6
import de.pewpewproject.lasertag.lasertaggame.state.synced.implementation.TeamsConfigState;
7
import de.pewpewproject.lasertag.lasertaggame.team.TeamDto;
8
import de.pewpewproject.lasertag.networking.NetworkingConstants;
9
import de.pewpewproject.lasertag.networking.server.ServerEventSending;
10
import io.netty.buffer.Unpooled;
11
import net.minecraft.network.PacketByteBuf;
12
import net.minecraft.server.world.ServerWorld;
13
import net.minecraft.util.math.BlockPos;
14
15
import java.util.ArrayList;
16
import java.util.HashMap;
17
import java.util.List;
18
19
/**
20
 * Implementation of ISpawnpointManager for the server lasertag game
21
 *
22
 * @author Étienne Muser
23
 */
24
public class SpawnpointManager implements ISpawnpointManager {
25
26
    private final HashMap<TeamDto, ArrayList<BlockPos>> spawnpointCache = new HashMap<>();
27
28
    private final ITeamsConfigState teamsConfigState;
29
30
    public SpawnpointManager(ITeamsConfigState teamsConfigState) {
31
        this.teamsConfigState = teamsConfigState;
32
    }
33
34
    /**
35
     * Gets all spawnpoints for the given team from the cache
36
     *
37
     * @param team
38
     * @return
39
     */
40
    @Override
41
    public ArrayList<BlockPos> getSpawnpoints(TeamDto team) {
42
        return spawnpointCache.get(team);
43
    }
44
45
    /**
46
     * Gets all spawnpoints for all teams in a unified list
47
     *
48
     * @return
49
     */
50
    @Override
51
    public List<BlockPos> getAllSpawnpoints() {
52
53
        return spawnpointCache.entrySet().stream()
54
                .flatMap(entry -> entry.getValue().stream())
55
                .toList();
56
    }
57
58
    @Override
59
    public void initSpawnpointCacheIfNecessary(ServerWorld world, boolean scanSpawnpoints) {
60
61
        if (spawnpointCache.isEmpty() || scanSpawnpoints) {
62
63
            initSpawnpointCache(world);
64
        }
65
    }
66
67
    @Override
68
    public void clearSpawnpointCache() {
69
        this.spawnpointCache.clear();
70
    }
71
72
    /**
73
     * Initializes the spawnpoint cache. Searches a 31 x 31 chunk area for spawnpoint blocks specified by the team.
74
     * This method is computationally intensive, don't call too often or when responsiveness is important. The call of this method blocks the server from ticking!
75
     */
76
    private void initSpawnpointCache(ServerWorld world) {
77
78
        try {
79
            this.spawnpointCache.clear();
80
81
            // Initialize team lists
82
            for (var team : teamsConfigState.getTeams()) {
83
84
                // Skip spectators
85
                if (team.equals(TeamsConfigState.SPECTATORS)) {
86
                    continue;
87
                }
88
89
                spawnpointCache.put(team, new ArrayList<>());
90
            }
91
92
            // Start time measurement
93
            var startTime = System.nanoTime();
94
95
            // Iterate over blocks and find spawnpoints
96
            world.fastSearchBlock((block, pos) -> {
97
                for (var teamDto : teamsConfigState.getTeams()) {
98
99
                    // Skip spectators
100
                    if (teamDto.equals(TeamsConfigState.SPECTATORS)) {
101
                        continue;
102
                    }
103
104
                    if (teamDto.spawnpointBlock().equals(block)) {
105
                        var team = spawnpointCache.get(teamDto);
106
                        synchronized (this) {
107
                            team.add(pos);
108
                        }
109
                        break;
110
                    }
111
                }
112
            }, (currChunk, maxChunk) -> {
113
                // Only send a progress update every second chunk to not ddos our players
114
                if (currChunk % 2 == 0) {
115
                    return;
116
                }
117
118
                // Create packet buffer
119
                var buf = new PacketByteBuf(Unpooled.buffer());
120
121
                // Write progress to buffer
122
                buf.writeDouble((double) currChunk / (double) maxChunk);
123
124
                ServerEventSending.sendToEveryone(world.getServer(), NetworkingConstants.PROGRESS, buf);
125
            });
126
127
            // Stop time measurement
128
            var stopTime = System.nanoTime();
129
            var duration = (stopTime - startTime) / 1000000000.0;
130
            LasertagMod.LOGGER.info("Spawnpoint search took " + duration + "s.");
131
        } catch (Exception ex) {
132
133
            LasertagMod.LOGGER.error("Unexpected error while scanning for spawnpoints: ", ex);
134
135
        } finally {
136
137
            // Create packet buffer
138
            var buf = new PacketByteBuf(Unpooled.buffer());
139
140
            // Write progress to buffer
141
            buf.writeDouble(-1.0F);
142
143
            ServerEventSending.sendToEveryone(world.getServer(), NetworkingConstants.PROGRESS, buf);
144
        }
145
    }
146
}
147