001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.command; 020 021import com.google.inject.Inject; 022import com.plotsquared.core.PlotSquared; 023import com.plotsquared.core.configuration.caption.StaticCaption; 024import com.plotsquared.core.configuration.caption.TranslatableCaption; 025import com.plotsquared.core.configuration.file.YamlConfiguration; 026import com.plotsquared.core.database.DBFunc; 027import com.plotsquared.core.database.Database; 028import com.plotsquared.core.database.MySQL; 029import com.plotsquared.core.database.SQLManager; 030import com.plotsquared.core.database.SQLite; 031import com.plotsquared.core.inject.annotations.WorldConfig; 032import com.plotsquared.core.listener.PlotListener; 033import com.plotsquared.core.player.PlotPlayer; 034import com.plotsquared.core.plot.Plot; 035import com.plotsquared.core.plot.PlotArea; 036import com.plotsquared.core.plot.PlotId; 037import com.plotsquared.core.plot.world.PlotAreaManager; 038import com.plotsquared.core.plot.world.SinglePlotArea; 039import com.plotsquared.core.util.EventDispatcher; 040import com.plotsquared.core.util.FileUtils; 041import com.plotsquared.core.util.query.PlotQuery; 042import com.plotsquared.core.util.task.TaskManager; 043import net.kyori.adventure.text.Component; 044import net.kyori.adventure.text.minimessage.tag.Tag; 045import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 046import org.checkerframework.checker.nullness.qual.NonNull; 047 048import java.io.File; 049import java.sql.SQLException; 050import java.util.ArrayList; 051import java.util.Arrays; 052import java.util.HashMap; 053import java.util.List; 054import java.util.Map.Entry; 055 056@CommandDeclaration(command = "database", 057 aliases = {"convert"}, 058 category = CommandCategory.ADMINISTRATION, 059 permission = "plots.database", 060 requiredType = RequiredType.CONSOLE, 061 usage = "/plot database [area] <sqlite | mysql | import>") 062public class DatabaseCommand extends SubCommand { 063 064 private final PlotAreaManager plotAreaManager; 065 private final EventDispatcher eventDispatcher; 066 private final PlotListener plotListener; 067 private final YamlConfiguration worldConfiguration; 068 069 @Inject 070 public DatabaseCommand( 071 final @NonNull PlotAreaManager plotAreaManager, 072 final @NonNull EventDispatcher eventDispatcher, 073 final @NonNull PlotListener plotListener, 074 @WorldConfig final @NonNull YamlConfiguration worldConfiguration 075 ) { 076 this.plotAreaManager = plotAreaManager; 077 this.eventDispatcher = eventDispatcher; 078 this.plotListener = plotListener; 079 this.worldConfiguration = worldConfiguration; 080 } 081 082 public static void insertPlots( 083 final SQLManager manager, final List<Plot> plots, 084 final PlotPlayer<?> player 085 ) { 086 TaskManager.runTaskAsync(() -> { 087 try { 088 ArrayList<Plot> ps = new ArrayList<>(plots); 089 player.sendMessage(TranslatableCaption.of("database.starting_conversion")); 090 manager.createPlotsAndData(ps, () -> { 091 player.sendMessage(TranslatableCaption.of("database.conversion_done")); 092 manager.close(); 093 }); 094 } catch (Exception e) { 095 player.sendMessage(TranslatableCaption.of("database.conversion_failed")); 096 e.printStackTrace(); 097 } 098 }); 099 } 100 101 @Override 102 public boolean onCommand(final PlotPlayer<?> player, String[] args) { 103 if (args.length < 1) { 104 player.sendMessage( 105 TranslatableCaption.of("commandconfig.command_syntax"), 106 TagResolver.resolver( 107 "value", 108 Tag.inserting(Component.text("/plot database [area] <sqlite | mysql | import>")) 109 ) 110 ); 111 return false; 112 } 113 List<Plot> plots; 114 PlotArea area = this.plotAreaManager.getPlotAreaByString(args[0]); 115 if (area != null) { 116 plots = PlotSquared.get().sortPlotsByTemp(area.getPlots()); 117 args = Arrays.copyOfRange(args, 1, args.length); 118 } else { 119 plots = PlotSquared.get().sortPlotsByTemp(PlotQuery.newQuery().allPlots().asList()); 120 } 121 if (args.length < 1) { 122 player.sendMessage( 123 TranslatableCaption.of("commandconfig.command_syntax"), 124 TagResolver.resolver("value", Tag.inserting(Component.text("/plot database [area] <sqlite|mysql|import>"))) 125 ); 126 player.sendMessage(TranslatableCaption.of("database.arg")); 127 return false; 128 } 129 try { 130 Database implementation; 131 String prefix = ""; 132 switch (args[0].toLowerCase()) { 133 case "import" -> { 134 if (args.length < 2) { 135 player.sendMessage( 136 TranslatableCaption.of("commandconfig.command_syntax"), 137 TagResolver.resolver( 138 "value", 139 Tag.inserting(Component.text("/plot database import <sqlite file> [prefix]")) 140 ) 141 ); 142 return false; 143 } 144 File file = FileUtils.getFile( 145 PlotSquared.platform().getDirectory(), 146 args[1].endsWith(".db") ? args[1] : args[1] + ".db" 147 ); 148 if (!file.exists()) { 149 player.sendMessage( 150 TranslatableCaption.of("database.does_not_exist"), 151 TagResolver.resolver("value", Tag.inserting(Component.text(file.toString()))) 152 ); 153 return false; 154 } 155 player.sendMessage(TranslatableCaption.of("database.starting_conversion")); 156 implementation = new SQLite(file); 157 SQLManager manager = new SQLManager(implementation, args.length == 3 ? args[2] : "", 158 this.eventDispatcher, this.plotListener, this.worldConfiguration 159 ); 160 HashMap<String, HashMap<PlotId, Plot>> map = manager.getPlots(); 161 plots = new ArrayList<>(); 162 for (Entry<String, HashMap<PlotId, Plot>> entry : map.entrySet()) { 163 String areaName = entry.getKey(); 164 PlotArea pa = this.plotAreaManager.getPlotAreaByString(areaName); 165 if (pa != null) { 166 for (Entry<PlotId, Plot> entry2 : entry.getValue().entrySet()) { 167 Plot plot = entry2.getValue(); 168 if (pa.getOwnedPlotAbs(plot.getId()) != null) { 169 if (pa instanceof SinglePlotArea) { 170 Plot newPlot = pa.getNextFreePlot(null, plot.getId()); 171 if (newPlot != null) { 172 PlotId newId = newPlot.getId(); 173 PlotId id = plot.getId(); 174 File worldFile = 175 new File( 176 PlotSquared.platform().worldContainer(), 177 id.toCommaSeparatedString() 178 ); 179 if (worldFile.exists()) { 180 File newFile = 181 new File( 182 PlotSquared.platform().worldContainer(), 183 newId.toCommaSeparatedString() 184 ); 185 worldFile.renameTo(newFile); 186 } 187 plot.setId(newId); 188 plot.setArea(pa); 189 plots.add(plot); 190 continue; 191 } 192 } 193 player.sendMessage( 194 TranslatableCaption.of("database.skipping_duplicated_plot"), 195 TagResolver.builder() 196 .tag("plot", Tag.inserting(Component.text(plot.toString()))) 197 .tag("id", Tag.inserting(Component.text(plot.temp))) 198 .build() 199 ); 200 continue; 201 } 202 plot.setArea(pa); 203 plots.add(plot); 204 } 205 } else { 206 HashMap<PlotId, Plot> plotMap = PlotSquared.get().plots_tmp 207 .computeIfAbsent(areaName, k -> new HashMap<>()); 208 plotMap.putAll(entry.getValue()); 209 } 210 } 211 DBFunc.createPlotsAndData( 212 plots, 213 () -> player.sendMessage(TranslatableCaption.of("database.conversion_done")) 214 ); 215 return true; 216 } 217 case "mysql" -> { 218 if (args.length < 6) { 219 player.sendMessage(StaticCaption.of( 220 "/plot database mysql [host] [port] [username] [password] [database] {prefix}")); 221 return false; 222 } 223 String host = args[1]; 224 String port = args[2]; 225 String username = args[3]; 226 String password = args[4]; 227 String database = args[5]; 228 if (args.length > 6) { 229 prefix = args[6]; 230 } 231 implementation = new MySQL(host, port, database, username, password); 232 } 233 case "sqlite" -> { 234 if (args.length < 2) { 235 player.sendMessage(StaticCaption.of("/plot database sqlite [file]")); 236 return false; 237 } 238 File sqliteFile = 239 FileUtils.getFile(PlotSquared.platform().getDirectory(), args[1] + ".db"); 240 implementation = new SQLite(sqliteFile); 241 } 242 default -> { 243 player.sendMessage(StaticCaption.of("/plot database [sqlite/mysql]")); 244 return false; 245 } 246 } 247 try { 248 SQLManager manager = new SQLManager( 249 implementation, 250 prefix, 251 this.eventDispatcher, 252 this.plotListener, 253 this.worldConfiguration 254 ); 255 DatabaseCommand.insertPlots(manager, plots, player); 256 return true; 257 } catch (ClassNotFoundException | SQLException e) { 258 player.sendMessage(TranslatableCaption.of("database.failed_to_save_plots")); 259 player.sendMessage(TranslatableCaption.of("errors.stacktrace_begin")); 260 e.printStackTrace(); 261 player.sendMessage(TranslatableCaption.of("errors.stacktrace_end")); 262 player.sendMessage(TranslatableCaption.of("database.invalid_args")); 263 return false; 264 } 265 } catch (ClassNotFoundException | SQLException e) { 266 player.sendMessage(TranslatableCaption.of("database.failed_to_open")); 267 player.sendMessage(TranslatableCaption.of("errors.stacktrace_begin")); 268 e.printStackTrace(); 269 player.sendMessage(TranslatableCaption.of("errors.stacktrace_end")); 270 player.sendMessage(TranslatableCaption.of("database.invalid_args")); 271 return false; 272 } 273 } 274 275}