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.minimessage.Template; 044import org.checkerframework.checker.nullness.qual.NonNull; 045 046import java.io.File; 047import java.sql.SQLException; 048import java.util.ArrayList; 049import java.util.Arrays; 050import java.util.HashMap; 051import java.util.List; 052import java.util.Map.Entry; 053 054@CommandDeclaration(command = "database", 055 aliases = {"convert"}, 056 category = CommandCategory.ADMINISTRATION, 057 permission = "plots.database", 058 requiredType = RequiredType.CONSOLE, 059 usage = "/plot database [area] <sqlite | mysql | import>") 060public class DatabaseCommand extends SubCommand { 061 062 private final PlotAreaManager plotAreaManager; 063 private final EventDispatcher eventDispatcher; 064 private final PlotListener plotListener; 065 private final YamlConfiguration worldConfiguration; 066 067 @Inject 068 public DatabaseCommand( 069 final @NonNull PlotAreaManager plotAreaManager, 070 final @NonNull EventDispatcher eventDispatcher, 071 final @NonNull PlotListener plotListener, 072 @WorldConfig final @NonNull YamlConfiguration worldConfiguration 073 ) { 074 this.plotAreaManager = plotAreaManager; 075 this.eventDispatcher = eventDispatcher; 076 this.plotListener = plotListener; 077 this.worldConfiguration = worldConfiguration; 078 } 079 080 public static void insertPlots( 081 final SQLManager manager, final List<Plot> plots, 082 final PlotPlayer<?> player 083 ) { 084 TaskManager.runTaskAsync(() -> { 085 try { 086 ArrayList<Plot> ps = new ArrayList<>(plots); 087 player.sendMessage(TranslatableCaption.of("database.starting_conversion")); 088 manager.createPlotsAndData(ps, () -> { 089 player.sendMessage(TranslatableCaption.of("database.conversion_done")); 090 manager.close(); 091 }); 092 } catch (Exception e) { 093 player.sendMessage(TranslatableCaption.of("database.conversion_failed")); 094 e.printStackTrace(); 095 } 096 }); 097 } 098 099 @Override 100 public boolean onCommand(final PlotPlayer<?> player, String[] args) { 101 if (args.length < 1) { 102 player.sendMessage( 103 TranslatableCaption.of("commandconfig.command_syntax"), 104 Template.of("value", "/plot database [area] <sqlite | mysql | import>") 105 ); 106 return false; 107 } 108 List<Plot> plots; 109 PlotArea area = this.plotAreaManager.getPlotAreaByString(args[0]); 110 if (area != null) { 111 plots = PlotSquared.get().sortPlotsByTemp(area.getPlots()); 112 args = Arrays.copyOfRange(args, 1, args.length); 113 } else { 114 plots = PlotSquared.get().sortPlotsByTemp(PlotQuery.newQuery().allPlots().asList()); 115 } 116 if (args.length < 1) { 117 player.sendMessage( 118 TranslatableCaption.of("commandconfig.command_syntax"), 119 Template.of("value", "/plot database [area] <sqlite|mysql|import>") 120 ); 121 player.sendMessage(TranslatableCaption.of("database.arg")); 122 return false; 123 } 124 try { 125 Database implementation; 126 String prefix = ""; 127 switch (args[0].toLowerCase()) { 128 case "import" -> { 129 if (args.length < 2) { 130 player.sendMessage( 131 TranslatableCaption.of("commandconfig.command_syntax"), 132 Template.of("value", "/plot database import <sqlite file> [prefix]") 133 ); 134 return false; 135 } 136 File file = FileUtils.getFile( 137 PlotSquared.platform().getDirectory(), 138 args[1].endsWith(".db") ? args[1] : args[1] + ".db" 139 ); 140 if (!file.exists()) { 141 player.sendMessage( 142 TranslatableCaption.of("database.does_not_exist"), 143 Template.of("value", String.valueOf(file)) 144 ); 145 return false; 146 } 147 player.sendMessage(TranslatableCaption.of("database.starting_conversion")); 148 implementation = new SQLite(file); 149 SQLManager manager = new SQLManager(implementation, args.length == 3 ? args[2] : "", 150 this.eventDispatcher, this.plotListener, this.worldConfiguration 151 ); 152 HashMap<String, HashMap<PlotId, Plot>> map = manager.getPlots(); 153 plots = new ArrayList<>(); 154 for (Entry<String, HashMap<PlotId, Plot>> entry : map.entrySet()) { 155 String areaName = entry.getKey(); 156 PlotArea pa = this.plotAreaManager.getPlotAreaByString(areaName); 157 if (pa != null) { 158 for (Entry<PlotId, Plot> entry2 : entry.getValue().entrySet()) { 159 Plot plot = entry2.getValue(); 160 if (pa.getOwnedPlotAbs(plot.getId()) != null) { 161 if (pa instanceof SinglePlotArea) { 162 Plot newPlot = pa.getNextFreePlot(null, plot.getId()); 163 if (newPlot != null) { 164 PlotId newId = newPlot.getId(); 165 PlotId id = plot.getId(); 166 File worldFile = 167 new File( 168 PlotSquared.platform().worldContainer(), 169 id.toCommaSeparatedString() 170 ); 171 if (worldFile.exists()) { 172 File newFile = 173 new File( 174 PlotSquared.platform().worldContainer(), 175 newId.toCommaSeparatedString() 176 ); 177 worldFile.renameTo(newFile); 178 } 179 plot.setId(newId); 180 plot.setArea(pa); 181 plots.add(plot); 182 continue; 183 } 184 } 185 player.sendMessage( 186 TranslatableCaption.of("database.skipping_duplicated_plot"), 187 Template.of("plot", String.valueOf(plot)), 188 Template.of("id", String.valueOf(plot.temp)) 189 ); 190 continue; 191 } 192 plot.setArea(pa); 193 plots.add(plot); 194 } 195 } else { 196 HashMap<PlotId, Plot> plotMap = PlotSquared.get().plots_tmp 197 .computeIfAbsent(areaName, k -> new HashMap<>()); 198 plotMap.putAll(entry.getValue()); 199 } 200 } 201 DBFunc.createPlotsAndData( 202 plots, 203 () -> player.sendMessage(TranslatableCaption.of("database.conversion_done")) 204 ); 205 return true; 206 } 207 case "mysql" -> { 208 if (args.length < 6) { 209 player.sendMessage(StaticCaption.of( 210 "/plot database mysql [host] [port] [username] [password] [database] {prefix}")); 211 return false; 212 } 213 String host = args[1]; 214 String port = args[2]; 215 String username = args[3]; 216 String password = args[4]; 217 String database = args[5]; 218 if (args.length > 6) { 219 prefix = args[6]; 220 } 221 implementation = new MySQL(host, port, database, username, password); 222 } 223 case "sqlite" -> { 224 if (args.length < 2) { 225 player.sendMessage(StaticCaption.of("/plot database sqlite [file]")); 226 return false; 227 } 228 File sqliteFile = 229 FileUtils.getFile(PlotSquared.platform().getDirectory(), args[1] + ".db"); 230 implementation = new SQLite(sqliteFile); 231 } 232 default -> { 233 player.sendMessage(StaticCaption.of("/plot database [sqlite/mysql]")); 234 return false; 235 } 236 } 237 try { 238 SQLManager manager = new SQLManager( 239 implementation, 240 prefix, 241 this.eventDispatcher, 242 this.plotListener, 243 this.worldConfiguration 244 ); 245 DatabaseCommand.insertPlots(manager, plots, player); 246 return true; 247 } catch (ClassNotFoundException | SQLException e) { 248 player.sendMessage(TranslatableCaption.of("database.failed_to_save_plots")); 249 player.sendMessage(TranslatableCaption.of("errors.stacktrace_begin")); 250 e.printStackTrace(); 251 player.sendMessage(TranslatableCaption.of("errors.stacktrace_end")); 252 player.sendMessage(TranslatableCaption.of("database.invalid_args")); 253 return false; 254 } 255 } catch (ClassNotFoundException | SQLException e) { 256 player.sendMessage(TranslatableCaption.of("database.failed_to_open")); 257 player.sendMessage(TranslatableCaption.of("errors.stacktrace_begin")); 258 e.printStackTrace(); 259 player.sendMessage(TranslatableCaption.of("errors.stacktrace_end")); 260 player.sendMessage(TranslatableCaption.of("database.invalid_args")); 261 return false; 262 } 263 } 264 265}