PageRenderTime 45ms CodeModel.GetById 36ms app.highlight 7ms RepoModel.GetById 0ms app.codeStats 1ms

/worldguard-legacy/src/main/java/com/sk89q/worldguard/protection/managers/RegionContainerImpl.java

https://gitlab.com/igserfurtmcschulserver/CustomWorldGuard
Java | 273 lines | 160 code | 29 blank | 84 comment | 10 complexity | cd34d9474cc31c9216d9fab5915a42d1 MD5 | raw file
  1/*
  2 * WorldGuard, a suite of tools for Minecraft
  3 * Copyright (C) sk89q <http://www.sk89q.com>
  4 * Copyright (C) WorldGuard team and contributors
  5 *
  6 * This program is free software: you can redistribute it and/or modify it
  7 * under the terms of the GNU Lesser General Public License as published by the
  8 * Free Software Foundation, either version 3 of the License, or
  9 * (at your option) any later version.
 10 *
 11 * This program is distributed in the hope that it will be useful, but WITHOUT
 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 14 * for more details.
 15 *
 16 * You should have received a copy of the GNU Lesser General Public License
 17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 18 */
 19
 20package com.sk89q.worldguard.protection.managers;
 21
 22import com.google.common.base.Supplier;
 23import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
 24import com.sk89q.worldguard.protection.managers.index.ChunkHashTable;
 25import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
 26import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
 27import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
 28import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
 29import com.sk89q.worldguard.protection.managers.storage.StorageException;
 30import com.sk89q.worldguard.util.Normal;
 31
 32import javax.annotation.Nullable;
 33import java.util.*;
 34import java.util.concurrent.ConcurrentHashMap;
 35import java.util.concurrent.ConcurrentMap;
 36import java.util.logging.Level;
 37import java.util.logging.Logger;
 38
 39import static com.google.common.base.Preconditions.checkNotNull;
 40
 41/**
 42 * Manages different {@link RegionManager}s for different worlds or dimensions.
 43 *
 44 * <p>This is an internal class. Do not use it.</p>
 45 */
 46public class RegionContainerImpl {
 47
 48    private static final Logger log = Logger.getLogger(RegionContainerImpl.class.getCanonicalName());
 49    private static final int LOAD_ATTEMPT_INTERVAL = 1000 * 30;
 50    private static final int SAVE_INTERVAL = 1000 * 30;
 51
 52    private final ConcurrentMap<Normal, RegionManager> mapping = new ConcurrentHashMap<Normal, RegionManager>();
 53    private final Object lock = new Object();
 54    private final RegionDriver driver;
 55    private final Supplier<? extends ConcurrentRegionIndex> indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory());
 56    private final Timer timer = new Timer();
 57    private final FlagRegistry flagRegistry;
 58
 59    private final Set<Normal> failingLoads = new HashSet<Normal>();
 60    private final Set<RegionManager> failingSaves = Collections.synchronizedSet(
 61            Collections.newSetFromMap(new WeakHashMap<RegionManager, Boolean>()));
 62
 63    /**
 64     * Create a new instance.
 65     *
 66     * @param driver the region store driver
 67     * @param flagRegistry the flag registry
 68     */
 69    public RegionContainerImpl(RegionDriver driver, FlagRegistry flagRegistry) {
 70        checkNotNull(driver);
 71        checkNotNull(flagRegistry, "flagRegistry");
 72        this.driver = driver;
 73        timer.schedule(new BackgroundLoader(), LOAD_ATTEMPT_INTERVAL, LOAD_ATTEMPT_INTERVAL);
 74        timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL);
 75        this.flagRegistry = flagRegistry;
 76    }
 77
 78    /**
 79     * Get the region store driver.
 80     *
 81     * @return the driver
 82     */
 83    public RegionDriver getDriver() {
 84        return driver;
 85    }
 86
 87    /**
 88     * Load the {@code RegionManager} for the world with the given name,
 89     * creating a new instance for the world if one does not exist yet.
 90     *
 91     * @param name the name of the world
 92     * @return a region manager, or {@code null} if loading failed
 93     */
 94    @Nullable
 95    public RegionManager load(String name) {
 96        checkNotNull(name);
 97
 98        Normal normal = Normal.normal(name);
 99
100        synchronized (lock) {
101            RegionManager manager = mapping.get(normal);
102            if (manager != null) {
103                return manager;
104            } else {
105                try {
106                    manager = createAndLoad(name);
107                    mapping.put(normal, manager);
108                    failingLoads.remove(normal);
109                    return manager;
110                } catch (StorageException e) {
111                    log.log(Level.WARNING, "Failed to load the region data for '" + name + "' (periodic attempts will be made to load the data until success)", e);
112                    failingLoads.add(normal);
113                    return null;
114                }
115            }
116        }
117    }
118
119    /**
120     * Create a new region manager and load the data.
121     *
122     * @param name the name of the world
123     * @return a region manager
124     * @throws StorageException thrown if loading fals
125     */
126    private RegionManager createAndLoad(String name) throws StorageException {
127        RegionDatabase store = driver.get(name);
128        RegionManager manager = new RegionManager(store, indexFactory, flagRegistry);
129        manager.load(); // Try loading, although it may fail
130        return manager;
131    }
132
133    /**
134     * Unload the region manager associated with the given world name.
135     *
136     * <p>If no region manager has been loaded for the given name, then
137     * nothing will happen.</p>
138     *
139     * @param name the name of the world
140     */
141    public void unload(String name) {
142        checkNotNull(name);
143
144        Normal normal = Normal.normal(name);
145
146        synchronized (lock) {
147            RegionManager manager = mapping.get(normal);
148            if (manager != null) {
149                try {
150                    manager.save();
151                } catch (StorageException e) {
152                    log.log(Level.WARNING, "Failed to save the region data for '" + name + "'", e);
153                }
154
155                mapping.remove(normal);
156                failingSaves.remove(manager);
157            }
158
159            failingLoads.remove(normal);
160        }
161    }
162
163    /**
164     * Unload all region managers and save their contents before returning.
165     * This message may block for an extended period of time.
166     */
167    public void unloadAll() {
168        synchronized (lock) {
169            for (Map.Entry<Normal, RegionManager> entry : mapping.entrySet()) {
170                String name = entry.getKey().toString();
171                RegionManager manager = entry.getValue();
172                try {
173                    manager.saveChanges();
174                } catch (StorageException e) {
175                    log.log(Level.WARNING, "Failed to save the region data for '" + name + "' while unloading the data for all worlds", e);
176                }
177            }
178
179            mapping.clear();
180            failingLoads.clear();
181            failingSaves.clear();
182        }
183    }
184
185    /**
186     * Get the region manager for the given world name.
187     *
188     * @param name the name of the world
189     * @return a region manager, or {@code null} if one was never loaded
190     */
191    @Nullable
192    public RegionManager get(String name) {
193        checkNotNull(name);
194        return mapping.get(Normal.normal(name));
195    }
196
197    /**
198     * Get an immutable list of loaded region managers.
199     *
200     * @return an immutable list
201     */
202    public List<RegionManager> getLoaded() {
203        return Collections.unmodifiableList(new ArrayList<RegionManager>(mapping.values()));
204    }
205
206    /**
207     * Get the a set of region managers that are failing to save.
208     *
209     * @return a set of region managers
210     */
211    public Set<RegionManager> getSaveFailures() {
212        return new HashSet<RegionManager>(failingSaves);
213    }
214
215    /**
216     * A task to save managers in the background.
217     */
218    private class BackgroundSaver extends TimerTask {
219        @Override
220        public void run() {
221            synchronized (lock) {
222                // Block loading of new region managers
223
224                for (Map.Entry<Normal, RegionManager> entry : mapping.entrySet()) {
225                    String name = entry.getKey().toString();
226                    RegionManager manager = entry.getValue();
227                    try {
228                        if (manager.saveChanges()) {
229                            log.info("Region data changes made in '" + name + "' have been background saved");
230                        }
231                        failingSaves.remove(manager);
232                    } catch (StorageException e) {
233                        failingSaves.add(manager);
234                        log.log(Level.WARNING, "Failed to save the region data for '" + name + "' during a periodical save", e);
235                    } catch (Exception e) {
236                        failingSaves.add(manager);
237                        log.log(Level.WARNING, "An expected error occurred during a periodical save", e);
238                    }
239                }
240            }
241        }
242    }
243
244    /**
245     * A task to re-try loading region data that has not yet been
246     * successfully loaded.
247     */
248    private class BackgroundLoader extends TimerTask {
249        @Override
250        public void run() {
251            synchronized (lock) {
252                if (!failingLoads.isEmpty()) {
253                    log.info("Attempting to load region data that has previously failed to load...");
254
255                    Iterator<Normal> it = failingLoads.iterator();
256                    while (it.hasNext()) {
257                        Normal normal = it.next();
258                        try {
259                            RegionManager manager = createAndLoad(normal.toString());
260                            mapping.put(normal, manager);
261                            it.remove();
262                            log.info("Successfully loaded region data for '" + normal.toString() + "'");
263                        } catch (StorageException e) {
264                            log.log(Level.WARNING, "Region data is still failing to load, at least for the world named '" + normal.toString() + "'", e);
265                            break;
266                        }
267                    }
268                }
269            }
270        }
271    }
272
273}