001package net.minecraft.world; 002 003import java.util.ArrayList; 004import java.util.Iterator; 005import java.util.List; 006import java.util.Random; 007import net.minecraft.block.Block; 008import net.minecraft.entity.Entity; 009import net.minecraft.util.Direction; 010import net.minecraft.util.LongHashMap; 011import net.minecraft.util.MathHelper; 012 013public class Teleporter 014{ 015 private final WorldServer worldServerInstance; 016 017 /** A private Random() function in Teleporter */ 018 private final Random random; 019 020 /** Stores successful portal placement locations for rapid lookup. */ 021 private final LongHashMap destinationCoordinateCache = new LongHashMap(); 022 023 /** 024 * A list of valid keys for the destinationCoordainteCache. These are based on the X & Z of the players initial 025 * location. 026 */ 027 private final List destinationCoordinateKeys = new ArrayList(); 028 029 public Teleporter(WorldServer par1WorldServer) 030 { 031 this.worldServerInstance = par1WorldServer; 032 this.random = new Random(par1WorldServer.getSeed()); 033 } 034 035 /** 036 * Place an entity in a nearby portal, creating one if necessary. 037 */ 038 public void placeInPortal(Entity par1Entity, double par2, double par4, double par6, float par8) 039 { 040 if (this.worldServerInstance.provider.dimensionId != 1) 041 { 042 if (!this.placeInExistingPortal(par1Entity, par2, par4, par6, par8)) 043 { 044 this.makePortal(par1Entity); 045 this.placeInExistingPortal(par1Entity, par2, par4, par6, par8); 046 } 047 } 048 else 049 { 050 int i = MathHelper.floor_double(par1Entity.posX); 051 int j = MathHelper.floor_double(par1Entity.posY) - 1; 052 int k = MathHelper.floor_double(par1Entity.posZ); 053 byte b0 = 1; 054 byte b1 = 0; 055 056 for (int l = -2; l <= 2; ++l) 057 { 058 for (int i1 = -2; i1 <= 2; ++i1) 059 { 060 for (int j1 = -1; j1 < 3; ++j1) 061 { 062 int k1 = i + i1 * b0 + l * b1; 063 int l1 = j + j1; 064 int i2 = k + i1 * b1 - l * b0; 065 boolean flag = j1 < 0; 066 this.worldServerInstance.setBlock(k1, l1, i2, flag ? Block.obsidian.blockID : 0); 067 } 068 } 069 } 070 071 par1Entity.setLocationAndAngles((double)i, (double)j, (double)k, par1Entity.rotationYaw, 0.0F); 072 par1Entity.motionX = par1Entity.motionY = par1Entity.motionZ = 0.0D; 073 } 074 } 075 076 /** 077 * Place an entity in a nearby portal which already exists. 078 */ 079 public boolean placeInExistingPortal(Entity par1Entity, double par2, double par4, double par6, float par8) 080 { 081 short short1 = 128; 082 double d3 = -1.0D; 083 int i = 0; 084 int j = 0; 085 int k = 0; 086 int l = MathHelper.floor_double(par1Entity.posX); 087 int i1 = MathHelper.floor_double(par1Entity.posZ); 088 long j1 = ChunkCoordIntPair.chunkXZ2Int(l, i1); 089 boolean flag = true; 090 double d4; 091 int k1; 092 093 if (this.destinationCoordinateCache.containsItem(j1)) 094 { 095 PortalPosition portalposition = (PortalPosition)this.destinationCoordinateCache.getValueByKey(j1); 096 d3 = 0.0D; 097 i = portalposition.posX; 098 j = portalposition.posY; 099 k = portalposition.posZ; 100 portalposition.lastUpdateTime = this.worldServerInstance.getTotalWorldTime(); 101 flag = false; 102 } 103 else 104 { 105 for (k1 = l - short1; k1 <= l + short1; ++k1) 106 { 107 double d5 = (double)k1 + 0.5D - par1Entity.posX; 108 109 for (int l1 = i1 - short1; l1 <= i1 + short1; ++l1) 110 { 111 double d6 = (double)l1 + 0.5D - par1Entity.posZ; 112 113 for (int i2 = this.worldServerInstance.getActualHeight() - 1; i2 >= 0; --i2) 114 { 115 if (this.worldServerInstance.getBlockId(k1, i2, l1) == Block.portal.blockID) 116 { 117 while (this.worldServerInstance.getBlockId(k1, i2 - 1, l1) == Block.portal.blockID) 118 { 119 --i2; 120 } 121 122 d4 = (double)i2 + 0.5D - par1Entity.posY; 123 double d7 = d5 * d5 + d4 * d4 + d6 * d6; 124 125 if (d3 < 0.0D || d7 < d3) 126 { 127 d3 = d7; 128 i = k1; 129 j = i2; 130 k = l1; 131 } 132 } 133 } 134 } 135 } 136 } 137 138 if (d3 >= 0.0D) 139 { 140 if (flag) 141 { 142 this.destinationCoordinateCache.add(j1, new PortalPosition(this, i, j, k, this.worldServerInstance.getTotalWorldTime())); 143 this.destinationCoordinateKeys.add(Long.valueOf(j1)); 144 } 145 146 double d8 = (double)i + 0.5D; 147 double d9 = (double)j + 0.5D; 148 d4 = (double)k + 0.5D; 149 int j2 = -1; 150 151 if (this.worldServerInstance.getBlockId(i - 1, j, k) == Block.portal.blockID) 152 { 153 j2 = 2; 154 } 155 156 if (this.worldServerInstance.getBlockId(i + 1, j, k) == Block.portal.blockID) 157 { 158 j2 = 0; 159 } 160 161 if (this.worldServerInstance.getBlockId(i, j, k - 1) == Block.portal.blockID) 162 { 163 j2 = 3; 164 } 165 166 if (this.worldServerInstance.getBlockId(i, j, k + 1) == Block.portal.blockID) 167 { 168 j2 = 1; 169 } 170 171 int k2 = par1Entity.getTeleportDirection(); 172 173 if (j2 > -1) 174 { 175 int l2 = Direction.rotateLeft[j2]; 176 int i3 = Direction.offsetX[j2]; 177 int j3 = Direction.offsetZ[j2]; 178 int k3 = Direction.offsetX[l2]; 179 int l3 = Direction.offsetZ[l2]; 180 boolean flag1 = !this.worldServerInstance.isAirBlock(i + i3 + k3, j, k + j3 + l3) || !this.worldServerInstance.isAirBlock(i + i3 + k3, j + 1, k + j3 + l3); 181 boolean flag2 = !this.worldServerInstance.isAirBlock(i + i3, j, k + j3) || !this.worldServerInstance.isAirBlock(i + i3, j + 1, k + j3); 182 183 if (flag1 && flag2) 184 { 185 j2 = Direction.rotateOpposite[j2]; 186 l2 = Direction.rotateOpposite[l2]; 187 i3 = Direction.offsetX[j2]; 188 j3 = Direction.offsetZ[j2]; 189 k3 = Direction.offsetX[l2]; 190 l3 = Direction.offsetZ[l2]; 191 k1 = i - k3; 192 d8 -= (double)k3; 193 int i4 = k - l3; 194 d4 -= (double)l3; 195 flag1 = !this.worldServerInstance.isAirBlock(k1 + i3 + k3, j, i4 + j3 + l3) || !this.worldServerInstance.isAirBlock(k1 + i3 + k3, j + 1, i4 + j3 + l3); 196 flag2 = !this.worldServerInstance.isAirBlock(k1 + i3, j, i4 + j3) || !this.worldServerInstance.isAirBlock(k1 + i3, j + 1, i4 + j3); 197 } 198 199 float f1 = 0.5F; 200 float f2 = 0.5F; 201 202 if (!flag1 && flag2) 203 { 204 f1 = 1.0F; 205 } 206 else if (flag1 && !flag2) 207 { 208 f1 = 0.0F; 209 } 210 else if (flag1 && flag2) 211 { 212 f2 = 0.0F; 213 } 214 215 d8 += (double)((float)k3 * f1 + f2 * (float)i3); 216 d4 += (double)((float)l3 * f1 + f2 * (float)j3); 217 float f3 = 0.0F; 218 float f4 = 0.0F; 219 float f5 = 0.0F; 220 float f6 = 0.0F; 221 222 if (j2 == k2) 223 { 224 f3 = 1.0F; 225 f4 = 1.0F; 226 } 227 else if (j2 == Direction.rotateOpposite[k2]) 228 { 229 f3 = -1.0F; 230 f4 = -1.0F; 231 } 232 else if (j2 == Direction.rotateRight[k2]) 233 { 234 f5 = 1.0F; 235 f6 = -1.0F; 236 } 237 else 238 { 239 f5 = -1.0F; 240 f6 = 1.0F; 241 } 242 243 double d10 = par1Entity.motionX; 244 double d11 = par1Entity.motionZ; 245 par1Entity.motionX = d10 * (double)f3 + d11 * (double)f6; 246 par1Entity.motionZ = d10 * (double)f5 + d11 * (double)f4; 247 par1Entity.rotationYaw = par8 - (float)(k2 * 90) + (float)(j2 * 90); 248 } 249 else 250 { 251 par1Entity.motionX = par1Entity.motionY = par1Entity.motionZ = 0.0D; 252 } 253 254 par1Entity.setLocationAndAngles(d8, d9, d4, par1Entity.rotationYaw, par1Entity.rotationPitch); 255 return true; 256 } 257 else 258 { 259 return false; 260 } 261 } 262 263 public boolean makePortal(Entity par1Entity) 264 { 265 byte b0 = 16; 266 double d0 = -1.0D; 267 int i = MathHelper.floor_double(par1Entity.posX); 268 int j = MathHelper.floor_double(par1Entity.posY); 269 int k = MathHelper.floor_double(par1Entity.posZ); 270 int l = i; 271 int i1 = j; 272 int j1 = k; 273 int k1 = 0; 274 int l1 = this.random.nextInt(4); 275 int i2; 276 double d1; 277 double d2; 278 int j2; 279 int k2; 280 int l2; 281 int i3; 282 int j3; 283 int k3; 284 int l3; 285 int i4; 286 int j4; 287 int k4; 288 double d3; 289 double d4; 290 291 for (i2 = i - b0; i2 <= i + b0; ++i2) 292 { 293 d1 = (double)i2 + 0.5D - par1Entity.posX; 294 295 for (j2 = k - b0; j2 <= k + b0; ++j2) 296 { 297 d2 = (double)j2 + 0.5D - par1Entity.posZ; 298 label274: 299 300 for (k2 = this.worldServerInstance.getActualHeight() - 1; k2 >= 0; --k2) 301 { 302 if (this.worldServerInstance.isAirBlock(i2, k2, j2)) 303 { 304 while (k2 > 0 && this.worldServerInstance.isAirBlock(i2, k2 - 1, j2)) 305 { 306 --k2; 307 } 308 309 for (i3 = l1; i3 < l1 + 4; ++i3) 310 { 311 l2 = i3 % 2; 312 k3 = 1 - l2; 313 314 if (i3 % 4 >= 2) 315 { 316 l2 = -l2; 317 k3 = -k3; 318 } 319 320 for (j3 = 0; j3 < 3; ++j3) 321 { 322 for (i4 = 0; i4 < 4; ++i4) 323 { 324 for (l3 = -1; l3 < 4; ++l3) 325 { 326 k4 = i2 + (i4 - 1) * l2 + j3 * k3; 327 j4 = k2 + l3; 328 int l4 = j2 + (i4 - 1) * k3 - j3 * l2; 329 330 if (l3 < 0 && !this.worldServerInstance.getBlockMaterial(k4, j4, l4).isSolid() || l3 >= 0 && !this.worldServerInstance.isAirBlock(k4, j4, l4)) 331 { 332 continue label274; 333 } 334 } 335 } 336 } 337 338 d4 = (double)k2 + 0.5D - par1Entity.posY; 339 d3 = d1 * d1 + d4 * d4 + d2 * d2; 340 341 if (d0 < 0.0D || d3 < d0) 342 { 343 d0 = d3; 344 l = i2; 345 i1 = k2; 346 j1 = j2; 347 k1 = i3 % 4; 348 } 349 } 350 } 351 } 352 } 353 } 354 355 if (d0 < 0.0D) 356 { 357 for (i2 = i - b0; i2 <= i + b0; ++i2) 358 { 359 d1 = (double)i2 + 0.5D - par1Entity.posX; 360 361 for (j2 = k - b0; j2 <= k + b0; ++j2) 362 { 363 d2 = (double)j2 + 0.5D - par1Entity.posZ; 364 label222: 365 366 for (k2 = this.worldServerInstance.getActualHeight() - 1; k2 >= 0; --k2) 367 { 368 if (this.worldServerInstance.isAirBlock(i2, k2, j2)) 369 { 370 while (k2 > 0 && this.worldServerInstance.isAirBlock(i2, k2 - 1, j2)) 371 { 372 --k2; 373 } 374 375 for (i3 = l1; i3 < l1 + 2; ++i3) 376 { 377 l2 = i3 % 2; 378 k3 = 1 - l2; 379 380 for (j3 = 0; j3 < 4; ++j3) 381 { 382 for (i4 = -1; i4 < 4; ++i4) 383 { 384 l3 = i2 + (j3 - 1) * l2; 385 k4 = k2 + i4; 386 j4 = j2 + (j3 - 1) * k3; 387 388 if (i4 < 0 && !this.worldServerInstance.getBlockMaterial(l3, k4, j4).isSolid() || i4 >= 0 && !this.worldServerInstance.isAirBlock(l3, k4, j4)) 389 { 390 continue label222; 391 } 392 } 393 } 394 395 d4 = (double)k2 + 0.5D - par1Entity.posY; 396 d3 = d1 * d1 + d4 * d4 + d2 * d2; 397 398 if (d0 < 0.0D || d3 < d0) 399 { 400 d0 = d3; 401 l = i2; 402 i1 = k2; 403 j1 = j2; 404 k1 = i3 % 2; 405 } 406 } 407 } 408 } 409 } 410 } 411 } 412 413 int i5 = l; 414 int j5 = i1; 415 j2 = j1; 416 int k5 = k1 % 2; 417 int l5 = 1 - k5; 418 419 if (k1 % 4 >= 2) 420 { 421 k5 = -k5; 422 l5 = -l5; 423 } 424 425 boolean flag; 426 427 if (d0 < 0.0D) 428 { 429 if (i1 < 70) 430 { 431 i1 = 70; 432 } 433 434 if (i1 > this.worldServerInstance.getActualHeight() - 10) 435 { 436 i1 = this.worldServerInstance.getActualHeight() - 10; 437 } 438 439 j5 = i1; 440 441 for (k2 = -1; k2 <= 1; ++k2) 442 { 443 for (i3 = 1; i3 < 3; ++i3) 444 { 445 for (l2 = -1; l2 < 3; ++l2) 446 { 447 k3 = i5 + (i3 - 1) * k5 + k2 * l5; 448 j3 = j5 + l2; 449 i4 = j2 + (i3 - 1) * l5 - k2 * k5; 450 flag = l2 < 0; 451 this.worldServerInstance.setBlock(k3, j3, i4, flag ? Block.obsidian.blockID : 0); 452 } 453 } 454 } 455 } 456 457 for (k2 = 0; k2 < 4; ++k2) 458 { 459 for (i3 = 0; i3 < 4; ++i3) 460 { 461 for (l2 = -1; l2 < 4; ++l2) 462 { 463 k3 = i5 + (i3 - 1) * k5; 464 j3 = j5 + l2; 465 i4 = j2 + (i3 - 1) * l5; 466 flag = i3 == 0 || i3 == 3 || l2 == -1 || l2 == 3; 467 this.worldServerInstance.setBlock(k3, j3, i4, flag ? Block.obsidian.blockID : Block.portal.blockID, 0, 2); 468 } 469 } 470 471 for (i3 = 0; i3 < 4; ++i3) 472 { 473 for (l2 = -1; l2 < 4; ++l2) 474 { 475 k3 = i5 + (i3 - 1) * k5; 476 j3 = j5 + l2; 477 i4 = j2 + (i3 - 1) * l5; 478 this.worldServerInstance.notifyBlocksOfNeighborChange(k3, j3, i4, this.worldServerInstance.getBlockId(k3, j3, i4)); 479 } 480 } 481 } 482 483 return true; 484 } 485 486 /** 487 * called periodically to remove out-of-date portal locations from the cache list. Argument par1 is a 488 * WorldServer.getTotalWorldTime() value. 489 */ 490 public void removeStalePortalLocations(long par1) 491 { 492 if (par1 % 100L == 0L) 493 { 494 Iterator iterator = this.destinationCoordinateKeys.iterator(); 495 long j = par1 - 600L; 496 497 while (iterator.hasNext()) 498 { 499 Long olong = (Long)iterator.next(); 500 PortalPosition portalposition = (PortalPosition)this.destinationCoordinateCache.getValueByKey(olong.longValue()); 501 502 if (portalposition == null || portalposition.lastUpdateTime < j) 503 { 504 iterator.remove(); 505 this.destinationCoordinateCache.remove(olong.longValue()); 506 } 507 } 508 } 509 } 510}