diff --git a/DeathSwap/config.yml b/DeathSwap/config.yml
new file mode 100644
index 0000000..0023eba
--- /dev/null
+++ b/DeathSwap/config.yml
@@ -0,0 +1,7 @@
+game:
+  minSwapTime: 25
+  maxSwapTime: 90
+  resistanceDuration: 6
+  spawnDistance: 1000
+  allowDrops: true
+  startKit: ""
\ No newline at end of file
diff --git a/DeathSwap/plugin.yml b/DeathSwap/plugin.yml
new file mode 100644
index 0000000..40d2a56
--- /dev/null
+++ b/DeathSwap/plugin.yml
@@ -0,0 +1,11 @@
+name: DeathSwap
+main: cz.fo2.chylex.deathswap.DeathSwap
+version: 1.1 beta
+author: chylex
+website: https://chylex.com
+load: POSTWORLD
+commands:
+  deathswap:
+    aliases: [ds,swap]
+    description: DeathSwap commands
+    usage: /deathswap
diff --git a/DeathSwap/src/cz/fo2/chylex/deathswap/CommandOverride.java b/DeathSwap/src/cz/fo2/chylex/deathswap/CommandOverride.java
new file mode 100644
index 0000000..7e848b8
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/deathswap/CommandOverride.java
@@ -0,0 +1,25 @@
+package cz.fo2.chylex.deathswap;
+import org.bukkit.ChatColor;
+import org.bukkit.command.CommandSender;
+
+public class CommandOverride{
+	public static boolean listCommand(DeathSwap plugin, CommandSender s){
+		if (plugin.swapper.isPlaying()){
+			StringBuilder alive = new StringBuilder(), dead = new StringBuilder();
+			for(String str : plugin.swapper.getPlayers()){
+				alive.append(str).append(", ");
+			}
+			for(String str : plugin.spectating.getSpectators()){
+				dead.append(str).append(", ");
+			}
+			if (alive.length() > 0){
+				s.sendMessage(ChatColor.GREEN + "[Alive] " + ChatColor.RESET + alive.substring(0, alive.length() - 2));
+			}
+			if (dead.length() > 0){
+				s.sendMessage(ChatColor.GREEN + "[Spectating] " + ChatColor.RESET + dead.substring(0, dead.length() - 2));
+			}
+			return true;
+		}
+		return false;
+	}
+}
diff --git a/DeathSwap/src/cz/fo2/chylex/deathswap/Config.java b/DeathSwap/src/cz/fo2/chylex/deathswap/Config.java
new file mode 100644
index 0000000..d7c41ca
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/deathswap/Config.java
@@ -0,0 +1,30 @@
+package cz.fo2.chylex.deathswap;
+import org.bukkit.ChatColor;
+import org.bukkit.configuration.file.FileConfiguration;
+
+public class Config{
+	private DeathSwap plugin;
+	public int min_swap_time, max_swap_time, resistance_duration, spawn_distance;
+	public boolean allow_drops;
+	public String spawn_items;
+	
+	public Config(DeathSwap plugin){
+		this.plugin = plugin;
+		plugin.saveDefaultConfig();
+	}
+	
+	public Config load(){
+		FileConfiguration conf = plugin.getConfig();
+		min_swap_time = conf.getInt("game.minSwapTime", 25);
+		max_swap_time = conf.getInt("game.maxSwapTime", 90);
+		resistance_duration = conf.getInt("game.resistanceDuration", 6);
+		spawn_distance = conf.getInt("game.spawnDistance", 1000);
+		allow_drops = conf.getBoolean("game.allowDrops", true);
+		spawn_items = conf.getString("game.startKit", "");
+		return this;
+	}
+	
+	public static String hl(String msg){
+		return new StringBuilder().append(ChatColor.RED).append(msg).append(ChatColor.RESET).toString();
+	}
+}
diff --git a/DeathSwap/src/cz/fo2/chylex/deathswap/DeathSwap.java b/DeathSwap/src/cz/fo2/chylex/deathswap/DeathSwap.java
new file mode 100644
index 0000000..8cea870
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/deathswap/DeathSwap.java
@@ -0,0 +1,76 @@
+package cz.fo2.chylex.deathswap;
+import java.util.Arrays;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.java.JavaPlugin;
+
+public class DeathSwap extends JavaPlugin implements Listener{
+	public Config config;
+	public Swapper swapper;
+	public Spectating spectating;
+	
+	@Override
+	public void onEnable(){
+		config = new Config(this).load();
+		swapper = new Swapper(this);
+		spectating = new Spectating(this);
+		new EventListener(this);
+	}
+	
+	@Override
+	public void onDisable(){
+	}
+	
+	@Override
+	public boolean onCommand(CommandSender s, Command cmd, String alias, String[] args){
+		if (s instanceof Player && ((Player)s).isOp() == false){
+			s.sendMessage("You don't have permissions!");
+			return true;
+		}
+		if (args.length == 0){
+			s.sendMessage(ChatColor.GREEN + "[DeathSwap commands]");
+			String a = "/" + alias + " ";
+			s.sendMessage(a + "start - start the game");
+			s.sendMessage(a + "resume - resume after server crash");
+			s.sendMessage(a + "reload - reload configuration file");
+		}
+		else if (args[0].equals("start")){
+			if (swapper.isPlaying()){
+				s.sendMessage("There's already a game running!");
+			}
+			else if (getServer().getOnlinePlayers().length < 2){
+				s.sendMessage("Not enough players to start the game!");
+			}
+			else{
+				getServer().getWorlds().get(0).setTime(0L);
+				getServer().broadcastMessage("Let the game begin!");
+				PlayerDistribution.distributeOnGrid(Arrays.asList(getServer().getOnlinePlayers()), getServer().getWorlds().get(0), config.spawn_distance);
+				swapper.start();
+			}
+		}
+		else if (args[0].equals("resume")){
+			if (swapper.isPlaying()){
+				s.sendMessage("There's already a game running!");
+			}
+			else if (getServer().getOnlinePlayers().length < 2){
+				s.sendMessage("Not enough players to start the game!");
+			}
+			else{
+				getServer().broadcastMessage("Let the game continue!");
+				swapper.start(false);
+			}
+		}
+		else if (args[0].equals("reload")){
+			reloadConfig();
+			config.load();
+			s.sendMessage("Configuration file was reloaded!");
+		}
+		else{
+			return false;
+		}
+		return true;
+	}
+}
diff --git a/DeathSwap/src/cz/fo2/chylex/deathswap/EventListener.java b/DeathSwap/src/cz/fo2/chylex/deathswap/EventListener.java
new file mode 100644
index 0000000..bbf4c0c
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/deathswap/EventListener.java
@@ -0,0 +1,81 @@
+package cz.fo2.chylex.deathswap;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerRespawnEvent;
+import org.bukkit.event.server.ServerCommandEvent;
+import cz.fo2.chylex.util.BukkitUtil;
+
+public class EventListener implements Listener{
+	private DeathSwap plugin;
+	
+	public EventListener(DeathSwap plugin){
+		this.plugin = plugin;
+		plugin.getServer().getPluginManager().registerEvents(this, plugin);
+	}
+	
+	@EventHandler
+	public void onPlayerLogin(PlayerJoinEvent e){
+		if (!plugin.swapper.canPlayerJoin(e.getPlayer().getName())){
+			plugin.spectating.addSpectator(e.getPlayer());
+		}
+		else if (plugin.swapper.isPlaying()){
+			plugin.swapper.onPlayerJoin(e.getPlayer().getName());
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerRespawn(PlayerRespawnEvent e){
+		if (plugin.swapper.isPlaying()){
+			plugin.spectating.addSpectator(e.getPlayer());
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerDisconnect(PlayerQuitEvent e){
+		if (plugin.swapper.canPlayerJoin(e.getPlayer().getName()) && plugin.swapper.isPlaying()){
+			plugin.swapper.onPlayerDisconnect(e.getPlayer().getName());
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerDeath(PlayerDeathEvent e){
+		if (plugin.swapper.isPlaying()){
+			plugin.swapper.onPlayerDeath(e.getEntity().getName());
+			if (!plugin.swapper.isPlaying() || !plugin.config.allow_drops){
+				e.setDroppedExp(0);
+				e.getDrops().clear();
+			}
+		}
+	}
+	
+	@EventHandler
+	public void onEntityDamage(EntityDamageEvent e){ // I HATE SPAWN SUFFOCATION!!!!!
+		if (e.getCause() == DamageCause.SUFFOCATION && e instanceof Player && plugin.swapper.canPlayerJoin(((Player)e.getEntity()).getName()) && plugin.swapper.isPlaying() && plugin.swapper.spawnProtection){
+			Location loc = e.getEntity().getLocation().clone();
+			BukkitUtil.getHighestBlockYAt(loc);
+			e.getEntity().teleport(loc);
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent e){
+		if (e.getMessage().startsWith("/list")){
+			e.setCancelled(CommandOverride.listCommand(plugin, e.getPlayer()));
+		}
+	}
+	
+	@EventHandler
+	public void onServerCommand(ServerCommandEvent e){
+		if (e.getCommand().startsWith("list")){
+			e.setCommand(CommandOverride.listCommand(plugin, e.getSender()) ? "" : "list");
+		}
+	}
+}
diff --git a/DeathSwap/src/cz/fo2/chylex/deathswap/PlayerDistribution.java b/DeathSwap/src/cz/fo2/chylex/deathswap/PlayerDistribution.java
new file mode 100644
index 0000000..3c2d0e3
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/deathswap/PlayerDistribution.java
@@ -0,0 +1,38 @@
+package cz.fo2.chylex.deathswap;
+import cz.fo2.chylex.util.BukkitUtil;
+import org.bukkit.Chunk;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class PlayerDistribution{
+	public static void distributeOnGrid(Collection<Player> players, World world, int distance){
+		int gridSize = 1, gridPls = players.size() - 9;
+		while(gridPls > 0){
+			gridPls -= (++gridSize) * 8;
+		}
+		List<Vector> locs = new ArrayList<Vector>();
+		for(int a = -gridSize; a <= gridSize; a++){
+			for(int b = -gridSize; b <= gridSize; b++){
+				locs.add(new Vector(a * distance, 0, b * distance));
+			}
+		}
+		Collections.shuffle(locs);
+		
+		for(Player p : players){
+			Location l = locs.get(0).toLocation(world);
+			Chunk c = world.getChunkAt(l);
+			if (!c.isLoaded()){
+				c.load(true);
+			}
+			BukkitUtil.getHighestBlockYAt(l);
+			p.teleport(l);
+			locs.remove(0);
+		}
+	}
+}
diff --git a/DeathSwap/src/cz/fo2/chylex/deathswap/Spectating.java b/DeathSwap/src/cz/fo2/chylex/deathswap/Spectating.java
new file mode 100644
index 0000000..fdf7dfe
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/deathswap/Spectating.java
@@ -0,0 +1,211 @@
+package cz.fo2.chylex.deathswap;
+import org.bukkit.GameMode;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.entity.EntityTargetEvent;
+import org.bukkit.event.entity.FoodLevelChangeEvent;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+import org.bukkit.event.player.PlayerDropItemEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerPickupItemEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.metadata.FixedMetadataValue;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Spectating implements Listener{
+	private DeathSwap plugin;
+	private Map<String, Integer> spectators = new ConcurrentHashMap<String, Integer>();
+	
+	public Spectating(DeathSwap plugin){
+		this.plugin = plugin;
+		plugin.getServer().getPluginManager().registerEvents(this, plugin);
+	}
+	
+	public Set<String> getSpectators(){
+		return Collections.unmodifiableSet(spectators.keySet());
+	}
+	
+	public void addSpectator(Player p){
+		final String name = p.getName();
+		spectators.put(name, -1);
+		p.setMetadata("DeathSwap_Spec", new FixedMetadataValue(plugin, true));
+		
+		plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable(){
+			@Override
+			public void run(){
+				Player p = plugin.getServer().getPlayerExact(name);
+				if (p == null){
+					return;
+				}
+				p.setAllowFlight(true);
+				p.setFlying(true);
+			}
+		}, 5L);
+		
+		p.setGameMode(GameMode.ADVENTURE);
+		for(Player pl : plugin.getServer().getOnlinePlayers()){
+			pl.hidePlayer(p);
+		}
+		
+		p.sendMessage("You're spectating. You can only chat with other spectators. Click to teleport between players.");
+	}
+	
+	public void removeSpectator(Player p){
+		spectators.remove(p.getName());
+		p.removeMetadata("DeathSwap_Spec", plugin);
+		
+		p.setAllowFlight(false);
+		p.setFlying(false);
+		p.setGameMode(GameMode.SURVIVAL);
+		for(Player pl : plugin.getServer().getOnlinePlayers()){
+			pl.showPlayer(p);
+		}
+	}
+	
+	public boolean isSpectator(Player p){
+		return p.hasMetadata("DeathSwap_Spec");
+	}
+	
+	public boolean isSpectator(String s){
+		return spectators.containsKey(s);
+	}
+	
+	@EventHandler
+	public void onPlayerLogin(PlayerJoinEvent e){
+		for(String s : spectators.keySet()){
+			Player p = plugin.getServer().getPlayerExact(s);
+			if (p != null){
+				e.getPlayer().hidePlayer(p);
+			}
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerDisconnect(PlayerQuitEvent e){
+		removeSpectator(e.getPlayer());
+	}
+	
+	@EventHandler
+	public void onAsyncPlayerChat(AsyncPlayerChatEvent e){
+		if (!isSpectator(e.getPlayer())){
+			return;
+		}
+		for(Iterator<Player> iter = e.getRecipients().iterator(); iter.hasNext(); ){
+			if (!isSpectator(iter.next())){
+				iter.remove();
+			}
+		}
+		e.setFormat("[Spectating] " + e.getFormat());
+	}
+	
+	@EventHandler
+	public void onBlockBreak(BlockBreakEvent e){
+		if (isSpectator(e.getPlayer())){
+			e.setCancelled(true);
+		}
+	}
+	
+	@EventHandler
+	public void onBlockPlace(BlockPlaceEvent e){
+		if (isSpectator(e.getPlayer())){
+			e.setCancelled(true);
+			e.getPlayer().getInventory().clear();
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerPickupItem(PlayerPickupItemEvent e){
+		if (isSpectator(e.getPlayer())){
+			e.setCancelled(true);
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerDropItem(PlayerDropItemEvent e){
+		if (isSpectator(e.getPlayer())){
+			e.setCancelled(true);
+			e.getPlayer().getInventory().clear();
+		}
+	}
+	
+	@EventHandler
+	public void onEntityDamage(EntityDamageEvent e){
+		if (e.getEntity() instanceof Player && isSpectator((Player)e.getEntity())){
+			e.setCancelled(true);
+		}
+	}
+	
+	@EventHandler
+	public void onEntityDamageByEntity(EntityDamageByEntityEvent e){
+		if (e.getDamager() instanceof Player && isSpectator((Player)e.getDamager())){
+			e.setCancelled(true);
+		}
+	}
+	
+	@EventHandler
+	public void onFoodLevelChange(FoodLevelChangeEvent e){
+		if (isSpectator((Player)e.getEntity())){
+			e.setFoodLevel(20);
+		}
+	}
+	
+	@EventHandler
+	public void onEntityTarget(EntityTargetEvent e){
+		if (e.getTarget() instanceof Player && isSpectator((Player)e.getTarget())){
+			e.setCancelled(true);
+		}
+	}
+	
+	@EventHandler
+	public void onPlayerInteract(PlayerInteractEvent e){
+		if (isSpectator(e.getPlayer())){
+			e.setCancelled(true);
+			int add = (e.getAction() == Action.LEFT_CLICK_AIR || e.getAction() == Action.LEFT_CLICK_BLOCK) ? 1 : (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) ? -1 : 0;
+			if (add == 0){
+				return;
+			}
+			
+			List<Player> list = new ArrayList<Player>();
+			for(String player : plugin.swapper.getPlayers()){
+				Player p = plugin.getServer().getPlayerExact(player);
+				if (p != null){
+					list.add(p);
+				}
+			}
+			int size = list.size();
+			if (size == 0){
+				return;
+			}
+			
+			Integer cur = spectators.get(e.getPlayer().getName());
+			if (cur == null){
+				cur = -1;
+			}
+			if (add == 1){
+				cur = cur + 1 < size ? cur + 1 : 0;
+			}
+			else if (add == -1){
+				cur = cur - 1 >= 0 ? cur - 1 : size - 1;
+			}
+			cur = Math.max(0, Math.min(size - 1, cur));
+			spectators.put(e.getPlayer().getName(), cur);
+			
+			Player target = list.get(cur);
+			e.getPlayer().teleport(target);
+			e.getPlayer().sendMessage("You were teleported to " + Config.hl(target.getName()) + "!");
+		}
+	}
+}
diff --git a/DeathSwap/src/cz/fo2/chylex/deathswap/Swapper.java b/DeathSwap/src/cz/fo2/chylex/deathswap/Swapper.java
new file mode 100644
index 0000000..0a736fd
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/deathswap/Swapper.java
@@ -0,0 +1,234 @@
+package cz.fo2.chylex.deathswap;
+import cz.fo2.chylex.util.BukkitUtil;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+public class Swapper{
+	private DeathSwap plugin;
+	private Set<String> players;
+	private Map<String, Byte> disconnected;
+	private Random rand;
+	private int taskId = -1;
+	private int time;
+	private PotionEffect resistanceEffect;
+	public boolean spawnProtection;
+	
+	public Swapper(DeathSwap plugin){
+		this.plugin = plugin;
+		players = new HashSet<String>();
+		disconnected = new HashMap<String, Byte>();
+		rand = new Random();
+		spawnProtection = false;
+	}
+	
+	public Set<String> getPlayers(){
+		return Collections.unmodifiableSet(players);
+	}
+	
+	public void start(){
+		start(true);
+	}
+	
+	public void start(boolean resetPlayers){
+		if (isPlaying()){
+			return;
+		}
+		renewTimer();
+		time += 8; // cooldown
+		resistanceEffect = new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 20 * plugin.config.resistance_duration, 5, true);
+		
+		spawnProtection = true;
+		plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable(){
+			@Override
+			public void run(){
+				spawnProtection = false;
+			}
+		}, 120L);
+		
+		players.clear();
+		disconnected.clear();
+		List<ItemStack> startItems = new ArrayList<ItemStack>();
+		for(String ar : plugin.config.spawn_items.split(",")){
+			try{
+				String[] data = ar.split(":");
+				String[] star = data[0].split("\\*");
+				int id = Integer.parseInt(star.length > 1 ? star[1] : star[0]);
+				int am = star.length > 1 ? Integer.parseInt(star[0]) : 1;
+				int dur = data.length > 1 ? Integer.parseInt(data[1]) : 0;
+				startItems.add(new ItemStack(id, am, (short)dur));
+			}catch(NumberFormatException e){
+			}
+		}
+		ItemStack[] startItemsArray = startItems.toArray(new ItemStack[startItems.size()]);
+		
+		PotionEffect startResistance = new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 200, 5, true);
+		for(Player p : plugin.getServer().getOnlinePlayers()){
+			players.add(p.getName());
+			if (resetPlayers){
+				BukkitUtil.resetPlayer(p);
+				p.addPotionEffect(startResistance);
+				p.getInventory().addItem(startItemsArray);
+			}
+		}
+		
+		taskId = plugin.getServer().getScheduler().runTaskTimer(plugin, new Runnable(){
+			@Override
+			public void run(){
+				if (disconnected.size() > 0){
+					for(String s : new HashSet<String>(disconnected.keySet())){
+						byte b = (byte)(disconnected.get(s) + 1);
+						if (b >= 60){
+							plugin.getServer().broadcastMessage(Config.hl(s) + " was disqualified");
+							onPlayerDeath(s);
+						}
+						else{
+							disconnected.put(s, b);
+						}
+					}
+				}
+				
+				if (--time <= 0){
+					swap();
+					renewTimer();
+				}
+			}
+		}, 20L, 20L).getTaskId();
+	}
+	
+	public boolean isPlaying(){
+		return taskId != -1;
+	}
+	
+	public boolean canPlayerJoin(String player){
+		return !isPlaying() || players.contains(player);
+	}
+	
+	public void onPlayerJoin(String player){
+		if (!isPlaying()){
+			return;
+		}
+		disconnected.remove(player);
+	}
+	
+	public void onPlayerDisconnect(String player){
+		if (!isPlaying()){
+			return;
+		}
+		disconnected.put(player, (byte)0);
+	}
+	
+	public void onPlayerDeath(String player){
+		if (!isPlaying()){
+			return;
+		}
+		players.remove(player);
+		updateGameState();
+	}
+	
+	public void updateGameState(){
+		int sz = players.size();
+		if (sz == 0){
+			plugin.getServer().broadcastMessage("Well, there are no players in the game, this shouldn't have happened... Awkward.");
+			end();
+		}
+		else if (sz == 1){
+			for(String s : plugin.spectating.getSpectators()){
+				Player p = plugin.getServer().getPlayerExact(s);
+				if (p != null){
+					plugin.spectating.removeSpectator(p);
+				}
+			}
+			String name = players.iterator().next();
+			plugin.getServer().broadcastMessage(Config.hl(name) + " is the winner!");
+			Location spawn = plugin.getServer().getWorlds().get(0).getSpawnLocation().clone();
+			spawn.setY(spawn.getWorld().getHighestBlockYAt(spawn.getBlockX(), spawn.getBlockZ()));
+			for(Player p : plugin.getServer().getOnlinePlayers()){
+				p.teleport(spawn);
+				if (p.getName().equals(name)){
+					BukkitUtil.resetPlayer(p);
+				}
+			}
+			end();
+		}
+	}
+	
+	public void end(){
+		plugin.getServer().getScheduler().cancelTask(taskId);
+		players.clear();
+		disconnected.clear();
+		taskId = -1;
+		spawnProtection = false;
+	}
+	
+	private void swap(){
+		List<Player> pls = new ArrayList<Player>();
+		for(String s : players){
+			Player p = plugin.getServer().getPlayerExact(s);
+			if (p != null){
+				pls.add(p);
+			}
+		}
+		int size = pls.size();
+		if (size < 2){
+			return;
+		}
+		else if (size == 2){
+			Player p1 = pls.get(0), p2 = pls.get(1);
+			Location l1 = p1.getLocation().clone(), l2 = p2.getLocation().clone();
+			preTeleport(p1);
+			preTeleport(p2);
+			p1.teleport(l2);
+			p2.teleport(l1);
+			postTeleport(p1);
+			postTeleport(p2);
+			p1.sendMessage("You were swapped with " + Config.hl(p2.getName()) + "!");
+			p2.sendMessage("You were swapped with " + Config.hl(p1.getName()) + "!");
+		}
+		else{
+			Collections.shuffle(pls, rand);
+			Player[] arrPlr = pls.toArray(new Player[size]);
+			Location[] arrLoc = new Location[size];
+			for(int i = 0; i < size; i++){
+				arrLoc[i] = arrPlr[i].getLocation().clone();
+			}
+			for(int i = 0; i < size; i++){
+				Player next = arrPlr[i + 1 == size ? 0 : i + 1], prev = arrPlr[i == 0 ? size - 1 : i - 1];
+				arrPlr[i].sendMessage("You're now at " + Config.hl(next.getName()) + "'s location, and " + Config.hl(prev.getName()) + " is on yours!");
+				preTeleport(arrPlr[i]);
+				arrPlr[i].teleport(arrLoc[i + 1 == size ? 0 : i + 1]);
+				postTeleport(arrPlr[i]);
+			}
+		}
+		for(String s : plugin.spectating.getSpectators()){
+			Player p = plugin.getServer().getPlayerExact(s);
+			if (p != null){
+				p.sendMessage("Players were swapped!");
+			}
+		}
+	}
+	
+	private void preTeleport(Player p){
+		if (p.isInsideVehicle()){
+			p.leaveVehicle();
+		}
+	}
+	
+	private void postTeleport(Player p){
+		p.addPotionEffect(resistanceEffect);
+	}
+	
+	private void renewTimer(){
+		time = rand.nextInt(Math.max(1, plugin.config.max_swap_time - plugin.config.min_swap_time)) + plugin.config.min_swap_time;
+	}
+}
diff --git a/DeathSwap/src/cz/fo2/chylex/util/BukkitUtil.java b/DeathSwap/src/cz/fo2/chylex/util/BukkitUtil.java
new file mode 100644
index 0000000..0355d8f
--- /dev/null
+++ b/DeathSwap/src/cz/fo2/chylex/util/BukkitUtil.java
@@ -0,0 +1,49 @@
+package cz.fo2.chylex.util;
+import org.bukkit.ChatColor;
+import org.bukkit.GameMode;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.potion.PotionEffect;
+import java.util.Arrays;
+
+public class BukkitUtil{
+	public static ItemStack setNameAndLore(ItemStack is, String name, String... lore){
+		ItemMeta meta = is.getItemMeta();
+		if (name != null){
+			meta.setDisplayName(ChatColor.RESET + name);
+		}
+		if (lore != null && lore.length > 0 && !lore[0].isEmpty()){
+			meta.setLore(Arrays.asList(lore));
+		}
+		is.setItemMeta(meta);
+		return is;
+	}
+	
+	public static void resetPlayer(Player p){
+		p.setFallDistance(0f);
+		p.setFireTicks(0);
+		p.setFoodLevel(20);
+		p.setGameMode(GameMode.SURVIVAL);
+		p.setHealth(20);
+		for(PotionEffect eff : p.getActivePotionEffects()){
+			p.removePotionEffect(eff.getType());
+		}
+		PlayerInventory inv = p.getInventory();
+		inv.clear();
+		inv.setHelmet(null);
+		inv.setChestplate(null);
+		inv.setLeggings(null);
+		inv.setBoots(null);
+	}
+	
+	public static void getHighestBlockYAt(Location loc){
+		loc.setY(128);
+		while(loc.getBlock().getTypeId() == 0){
+			loc.setY(loc.getY() - 1);
+		}
+		loc.setY(loc.getY() + 2D);
+	}
+}
diff --git a/README.md b/README.md
index e2f9edd..c42bf9f 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,5 @@
 My old Bukkit plugins that probably don't work anymore. They have been open-sourced so feel free to update and re-release them as long as you follow the license.
+
+| Plugin | Link | State | Changes |
+| ------ | ---- | ----- | ------- |
+| Ultra-Death  Swap | [CurseForge](https://www.curseforge.com/minecraft/bukkit-plugins/ultra-deathswap) | Made for 1.15.2, needs API updates. | Reformatted & updated `plugin.yml`. |