Passed
Pull Request — master (#167)
by Vincent
10:23
created

FightService(MapTemplateRepository,Dispatcher,Collection,Collection,FightConfiguration)   A

Complexity

Conditions 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 11
ccs 9
cts 9
cp 1
crap 1
rs 9.85
c 0
b 0
f 0
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2020 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.game.fight;
21
22
import fr.quatrevieux.araknemu.core.event.Dispatcher;
23
import fr.quatrevieux.araknemu.core.event.EventsSubscriber;
24
import fr.quatrevieux.araknemu.core.event.Listener;
25
import fr.quatrevieux.araknemu.data.world.repository.environment.MapTemplateRepository;
26
import fr.quatrevieux.araknemu.game.GameConfiguration;
27
import fr.quatrevieux.araknemu.game.event.GameStopped;
28
import fr.quatrevieux.araknemu.game.exploration.event.ExplorationPlayerCreated;
29
import fr.quatrevieux.araknemu.game.exploration.map.ExplorationMap;
30
import fr.quatrevieux.araknemu.game.fight.builder.FightBuilder;
31
import fr.quatrevieux.araknemu.game.fight.builder.FightBuilderFactory;
32
import fr.quatrevieux.araknemu.game.fight.event.FightCreated;
33
import fr.quatrevieux.araknemu.game.fight.map.FightMap;
34
import fr.quatrevieux.araknemu.game.fight.module.FightModule;
35
import fr.quatrevieux.araknemu.game.listener.player.exploration.LeaveExplorationForFight;
36
import fr.quatrevieux.araknemu.game.listener.player.fight.AttachFighter;
37
import fr.quatrevieux.araknemu.game.player.event.PlayerLoaded;
38
39
import java.util.Collection;
40
import java.util.Collections;
41
import java.util.Map;
42
import java.util.NoSuchElementException;
43
import java.util.concurrent.ConcurrentHashMap;
44
import java.util.concurrent.Executors;
45
import java.util.concurrent.ScheduledExecutorService;
46
import java.util.concurrent.atomic.AtomicInteger;
47
import java.util.function.Function;
48
import java.util.stream.Collectors;
49
50
/**
51
 * Service for create fights
52
 */
53
public final class FightService implements EventsSubscriber {
54
    private final MapTemplateRepository mapRepository;
55
    private final Dispatcher dispatcher;
56
    private final Map<Class, FightBuilderFactory> builderFactories;
57
    private final Collection<FightModule.Factory> moduleFactories;
58
    private final GameConfiguration.FightConfiguration configuration;
0 ignored issues
show
Unused Code introduced by
Consider removing the unused private field configuration.
Loading history...
59
    private final ScheduledExecutorService executor;
60
61 1
    private final Map<Integer, Map<Integer, Fight>> fightsByMapId = new ConcurrentHashMap<>();
62 1
    private final AtomicInteger lastFightId = new AtomicInteger();
63
64 1
    public FightService(MapTemplateRepository mapRepository, Dispatcher dispatcher, Collection<? extends FightBuilderFactory> factories, Collection<FightModule.Factory> moduleFactories, GameConfiguration.FightConfiguration configuration) {
65 1
        this.mapRepository = mapRepository;
66 1
        this.dispatcher = dispatcher;
67 1
        this.moduleFactories = moduleFactories;
68 1
        this.configuration = configuration;
69 1
        this.executor = Executors.newScheduledThreadPool(configuration.threadsCount());
70
71 1
        this.builderFactories = factories.stream().collect(
72 1
            Collectors.toMap(
73
                FightBuilderFactory::type,
74 1
                Function.identity()
75
            )
76
        );
77 1
    }
78
79
    @Override
80
    public Listener[] listeners() {
81 1
        return new Listener[] {
82 1
            new Listener<PlayerLoaded>() {
83
                @Override
84
                public void on(PlayerLoaded event) {
85 1
                    event.player().dispatcher().add(new AttachFighter(event.player()));
86 1
                }
87
88
                @Override
89
                public Class<PlayerLoaded> event() {
90 1
                    return PlayerLoaded.class;
91
                }
92
            },
93 1
            new Listener<ExplorationPlayerCreated>() {
94
                @Override
95
                public void on(ExplorationPlayerCreated event) {
96 1
                    event.player().dispatcher().add(new LeaveExplorationForFight(event.player()));
97 1
                }
98
99
                @Override
100
                public Class<ExplorationPlayerCreated> event() {
101 1
                    return ExplorationPlayerCreated.class;
102
                }
103
            },
104 1
            new Listener<GameStopped>() {
105
                @Override
106
                public void on(GameStopped event) {
107 1
                    fightsByMapId.values().stream()
108 1
                        .flatMap(fights -> fights.values().stream())
109 1
                        .forEach(fight -> fight.cancel(true))
110
                    ;
111
112 1
                    fightsByMapId.clear();
113 1
                    executor.shutdownNow();
114 1
                }
115
116
                @Override
117
                public Class<GameStopped> event() {
118 1
                    return GameStopped.class;
119
                }
120
            },
121
        };
122
    }
123
124
    /**
125
     * Create fight map
126
     *
127
     * @param map The base map
128
     */
129
    public FightMap map(ExplorationMap map) {
130 1
        return new FightMap(mapRepository.get(map.id()));
131
    }
132
133
    /**
134
     * Create the fight handler
135
     *
136
     * @param type The build type
137
     */
138
    @SuppressWarnings("unchecked")
139
    public <B extends FightBuilder> FightHandler<B> handler(Class<B> type) {
140 1
        return new FightHandler<>(
141
            this,
142 1
            (B) builderFactories.get(type).create(this, executor)
143
        );
144
    }
145
146
    /**
147
     * Get all fights on the map
148
     *
149
     * @param mapId The map id
150
     */
151
    public Collection<Fight> fightsByMap(int mapId) {
152 1
        if (fightsByMapId.containsKey(mapId)) {
153 1
            return fightsByMapId.get(mapId).values();
154
        }
155
156 1
        return Collections.emptyList();
157
    }
158
159
    /**
160
     * Get all available fights
161
     * Note: this method can be really heavy to execute
162
     */
163
    public Collection<Fight> fights() {
164 1
        return fightsByMapId.values().stream()
165 1
            .flatMap(fights -> fights.values().stream())
166 1
            .collect(Collectors.toList())
167
        ;
168
    }
169
170
    /**
171
     * Get a fight by its id from a map
172
     *
173
     * @param mapId The map id
174
     * @param fightId The fight id
175
     */
176
    public Fight getFromMap(int mapId, int fightId) {
177 1
        if (!fightsByMapId.containsKey(mapId)) {
178 1
            throw new NoSuchElementException("Fight not found");
179
        }
180
181 1
        final Map<Integer, Fight> fights = fightsByMapId.get(mapId);
182
183 1
        if (!fights.containsKey(fightId)) {
184 1
            throw new NoSuchElementException("Fight not found");
185
        }
186
187 1
        return fights.get(fightId);
188
    }
189
190
    /**
191
     * Generate a new unique fight id
192
     */
193
    int newFightId() {
194 1
        return lastFightId.incrementAndGet();
195
    }
196
197
    /**
198
     * The fight is initialized
199
     */
200
    synchronized void created(Fight fight) {
201 1
        if (fightsByMapId.containsKey(fight.map().id())) {
202 1
            fightsByMapId.get(fight.map().id()).put(fight.id(), fight);
203
        } else {
204 1
            final Map<Integer, Fight> fights = new ConcurrentHashMap<>();
205
206 1
            fights.put(fight.id(), fight);
207
208 1
            fightsByMapId.put(fight.map().id(), fights);
209
        }
210
211 1
        dispatcher.dispatch(new FightCreated(fight));
212 1
    }
213
214
    /**
215
     * Remove the fight
216
     */
217
    synchronized void remove(Fight fight) {
218 1
        fightsByMapId.get(fight.map().id()).remove(fight.id());
219 1
    }
220
221
    /**
222
     * Make modules for a fight
223
     */
224
    Collection<FightModule> modules(Fight fight) {
225 1
        return moduleFactories.stream()
226 1
            .map(factory -> factory.create(fight))
227 1
            .collect(Collectors.toList())
228
        ;
229
    }
230
}
231