001/* 002 * Trident - A Multithreaded Server Alternative 003 * Copyright 2014 The TridentSDK Team 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package net.tridentsdk.server.chunk; 018 019import com.google.common.collect.Lists; 020import com.google.common.collect.Maps; 021import net.tridentsdk.server.concurrent.ThreadsHandler; 022import net.tridentsdk.server.world.TridentChunk; 023import net.tridentsdk.server.world.TridentWorld; 024import net.tridentsdk.world.ChunkLocation; 025 026import javax.annotation.concurrent.GuardedBy; 027import javax.annotation.concurrent.ThreadSafe; 028import java.util.Collection; 029import java.util.HashMap; 030import java.util.Set; 031import java.util.function.Consumer; 032 033/** 034 * Manages the chunks stored in memory per world 035 * 036 * @author The TridentSDK Team 037 */ 038@ThreadSafe 039public class ChunkHandler { 040 @GuardedBy("counters") 041 private final HashMap<ChunkLocation, CRefCounter> counters = Maps.newHashMap(); 042 private final TridentWorld world; 043 044 /** 045 * Creates a new chunk handler to manage the chunks of the provided world 046 * 047 * @param world the world to manage chunks for 048 */ 049 public ChunkHandler(TridentWorld world) { 050 this.world = world; 051 } 052 053 /** 054 * Places a chunk into the collection of in-memory chunks 055 * 056 * @param chunk the chunk to add 057 */ 058 public void put(TridentChunk chunk) { 059 synchronized (counters) { 060 counters.put(chunk.location(), CRefCounter.wrap(chunk)); 061 } 062 } 063 064 /** 065 * Obtains the chunk at the given location in the world, generating if given to do so 066 * 067 * @param location the location to obtain the chunk 068 * @param gen {@code true} to generate a new chunk if no chunk exists 069 * @return the chunk at the given location, or {@code null} if it doesn't exist and {@code gen} is false 070 */ 071 public TridentChunk get(ChunkLocation location, boolean gen) { 072 if (gen) { 073 synchronized (counters) { 074 CRefCounter counter = counters.get(location); 075 if (counter == null) { 076 return world.generateChunk(location); 077 } else return counter.unwrap(); 078 } 079 } else { 080 CRefCounter refCounter = get(location); 081 return refCounter == null ? null : refCounter.unwrap(); 082 } 083 } 084 085 /** 086 * Obtains the chunk reference counter at the specified location 087 * 088 * @param location the location to obtain the counter 089 * @return the counter at the location, or {@code null} if it doesn't exist 090 */ 091 public CRefCounter get(ChunkLocation location) { 092 synchronized (counters) { 093 return counters.get(location); 094 } 095 } 096 097 /** 098 * Obtains the chunk reference counter and applies a transformation function 099 * 100 * @param location the location or obtain the chunk reference counter 101 * @param consumer the transformation function 102 * @return {@code true} to indicate that the chunk was successfully retrieved and transformed 103 */ 104 public boolean apply(ChunkLocation location, Consumer<CRefCounter> consumer) { 105 CRefCounter chunk = get(location); 106 if (chunk != null) { 107 consumer.accept(chunk); 108 return true; 109 } 110 111 return false; 112 } 113 114 /** 115 * Attempts to remove the chunk from memory and save it 116 * 117 * <p>This method returns {@code false} if: 118 * <ul> 119 * <li>The chunk is not loaded</li> 120 * <li>The chunk still has strong references</li> 121 * </ul></p> 122 * 123 * @param location the location to remove the chunk 124 * @return {@code true} to signify that the collection was modified as a result of this operation 125 */ 126 public boolean tryRemove(ChunkLocation location) { 127 synchronized (counters) { 128 CRefCounter chunk = get(location); 129 if (chunk == null) { 130 return false; 131 } 132 133 if (!chunk.hasStrongRefs()) { 134 TridentChunk c = chunk.unwrap(); 135 if (chunk.hasWeakRefs()) { 136 // TODO remove weak referencing items 137 } 138 139 ThreadsHandler.chunkExecutor().execute(c::unload); 140 remove(location); 141 return true; 142 } 143 } 144 145 return false; 146 } 147 148 /** 149 * Releases the reference counters associated with the chunks that are specified in the set given 150 * 151 * @param chunkSet the set of chunks to release references to, given that they exist in this cache 152 */ 153 public void releaseReferences(ChunkLocationSet chunkSet) { 154 synchronized (counters) { 155 for (ChunkLocation location : chunkSet.locations()) { 156 CRefCounter counter = get(location); 157 if (counter != null) { 158 counter.releaseStrong(); 159 } 160 } 161 } 162 } 163 164 /** 165 * Manually removes the chunk from the collection without running any cleanup code 166 * 167 * @param location the location to remove the chunk from 168 */ 169 public void remove(ChunkLocation location) { 170 synchronized (counters) { 171 counters.remove(location); 172 } 173 } 174 175 /** 176 * Obtains the set of chunk locations that have already been loaded 177 * 178 * @return the set of loaded chunk locations 179 */ 180 public Set<ChunkLocation> keys() { 181 synchronized (counters) { 182 return counters.keySet(); 183 } 184 } 185 186 /** 187 * Obtains the chunks that have been loaded into memory 188 * 189 * @return the collection of loaded in-memory chunks 190 */ 191 public Collection<TridentChunk> values() { 192 Collection<TridentChunk> chunks = Lists.newArrayList(); 193 synchronized (counters) { 194 counters.values().stream().forEach(c -> chunks.add(c.unwrap())); 195 } 196 return chunks; 197 } 198 199 /** 200 * Obtains the amount of loaded chunks 201 * 202 * @return the amount of loaded chunks 203 */ 204 public int size() { 205 synchronized (counters) { 206 return counters.size(); 207 } 208 } 209}