1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of Dedipanel project |
5
|
|
|
* |
6
|
|
|
* (c) 2010-2015 Dedipanel <http://www.dedicated-panel.net> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace DP\GameServer\MinecraftServerBundle\Entity; |
13
|
|
|
|
14
|
|
|
use Doctrine\ORM\Mapping as ORM; |
15
|
|
|
use DP\GameServer\GameServerBundle\Entity\GameServer; |
16
|
|
|
use Symfony\Component\Validator\Constraints as Assert; |
17
|
|
|
use DP\Core\MachineBundle\PHPSeclibWrapper\PHPSeclibWrapper; |
18
|
|
|
use DP\Core\GameBundle\Entity\Plugin; |
19
|
|
|
use DP\Core\CoreBundle\Exception\MissingPacketException; |
20
|
|
|
use Symfony\Component\Validator\Mapping\ClassMetadata; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* DP\GameServer\MinecraftServerBundle\Entity\MinecraftServer |
24
|
|
|
* |
25
|
|
|
* @ORM\Table(name="minecraft_server") |
26
|
|
|
* @ORM\Entity(repositoryClass="DP\GameServer\GameServerBundle\Entity\GameServerRepository") |
27
|
|
|
* |
28
|
|
|
* @todo: refacto phpseclib |
29
|
|
|
* @todo: refacto domain logic |
30
|
|
|
*/ |
31
|
|
|
class MinecraftServer extends GameServer |
32
|
|
|
{ |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var integer $queryPort |
36
|
|
|
* |
37
|
|
|
* @ORM\Column(name="queryPort", type="integer", nullable=true) |
38
|
|
|
*/ |
39
|
|
|
protected $queryPort; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var integer $rconPort |
43
|
|
|
* |
44
|
|
|
* @ORM\Column(name="rconPort", type="integer") |
45
|
|
|
*/ |
46
|
|
|
protected $rconPort; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var integer $minHeap |
50
|
|
|
* |
51
|
|
|
* @ORM\Column(name="minHeap", type="integer") |
52
|
|
|
*/ |
53
|
|
|
protected $minHeap; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @var integer $maxHeap |
57
|
|
|
* |
58
|
|
|
* @ORM\Column(name="maxHeap", type="integer") |
59
|
|
|
*/ |
60
|
|
|
protected $maxHeap; |
61
|
|
|
|
62
|
|
|
/* |
63
|
|
|
* Set minecraft query port |
64
|
|
|
* |
65
|
|
|
* @param integer $queryPort |
66
|
|
|
*/ |
67
|
|
|
public function setQueryPort($queryPort) |
68
|
|
|
{ |
69
|
|
|
$this->queryPort = $queryPort; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/* |
73
|
|
|
* Get minecraft query port |
74
|
|
|
* |
75
|
|
|
* @return integer Query port |
76
|
|
|
*/ |
77
|
|
|
public function getQueryPort() |
78
|
|
|
{ |
79
|
|
|
return $this->queryPort; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/* |
83
|
|
|
* Set rcon port |
84
|
|
|
* |
85
|
|
|
* @param integer $rconPort |
86
|
|
|
*/ |
87
|
|
|
public function setRconPort($rconPort) |
88
|
|
|
{ |
89
|
|
|
$this->rconPort = $rconPort; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/* |
93
|
|
|
* Get rcon port |
94
|
|
|
* |
95
|
|
|
* @return integer RCON Port |
96
|
|
|
*/ |
97
|
|
|
public function getRconPort() |
98
|
|
|
{ |
99
|
|
|
return $this->rconPort; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Set min heap |
104
|
|
|
* |
105
|
|
|
* @param integer $minHeap |
106
|
|
|
*/ |
107
|
|
|
public function setMinHeap($minHeap) |
108
|
|
|
{ |
109
|
|
|
$this->minHeap = $minHeap; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Get min heap |
114
|
|
|
* |
115
|
|
|
* @return integer Min heap |
116
|
|
|
*/ |
117
|
|
|
public function getMinHeap() |
118
|
|
|
{ |
119
|
|
|
return $this->minHeap; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Set max heap |
124
|
|
|
* |
125
|
|
|
* @param integer $maxHeap |
126
|
|
|
*/ |
127
|
|
|
public function setMaxHeap($maxHeap) |
128
|
|
|
{ |
129
|
|
|
$this->maxHeap = $maxHeap; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Get max heap |
134
|
|
|
* |
135
|
|
|
* @return integer Max heap |
136
|
|
|
*/ |
137
|
|
|
public function getMaxHeap() |
138
|
|
|
{ |
139
|
|
|
return $this->maxHeap; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Download server |
144
|
|
|
*/ |
145
|
|
|
public function installServer(\Twig_Environment $twig) |
146
|
|
|
{ |
147
|
|
|
$conn = $this->getMachine()->getConnection(); |
148
|
|
|
|
149
|
|
|
if (!$conn->isJavaInstalled()) { |
150
|
|
|
throw new MissingPacketException('oracle-java8-installer'); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
$installDir = $this->getAbsoluteDir(); |
154
|
|
|
$logPath = $installDir . 'install.log'; |
155
|
|
|
|
156
|
|
|
$conn->mkdir($installDir); |
157
|
|
|
|
158
|
|
|
$dlUrl = 'https://s3.amazonaws.com/MinecraftDownload/launcher/minecraft_server.jar'; |
159
|
|
|
if ($this->game->getLaunchName() == 'bukkit') { |
160
|
|
|
$dlUrl = 'http://dl.bukkit.org/latest-rb/craftbukkit.jar'; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$conn->exec('cd ' . $installDir . ' && wget -N -o ' . $logPath . ' ' . $dlUrl . ' &'); |
164
|
|
|
|
165
|
|
|
$this->installationStatus = 0; |
166
|
|
|
|
167
|
|
|
return true; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
public function getInstallationProgress() |
171
|
|
|
{ |
172
|
|
|
$installDir = $this->getAbsoluteDir(); |
173
|
|
|
$logPath = $installDir . 'install.log'; |
174
|
|
|
$binPath = $installDir . $this->getGame()->getBin(); |
175
|
|
|
$conn = $this->getMachine()->getConnection(); |
176
|
|
|
|
177
|
|
|
$status = $conn->exec("if [ -d $installDir ]; then if [ -e $logPath ]; then echo 1; elif [ -e $binPath ]; then echo 2; else echo 0; fi; else echo 0; fi;"); |
178
|
|
|
|
179
|
|
|
if ($status == 2) { |
180
|
|
|
return 100; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
// On récupère les 20 dernières lignes du fichier afin de déterminer le pourcentage |
184
|
|
|
$installLog = $conn->exec('tail -n 20 ' . $logPath); |
185
|
|
|
$percent = $this->getPercentFromInstallLog($installLog); |
186
|
|
|
|
187
|
|
|
// Suppression du fichier de log si le dl est terminé |
188
|
|
|
if (!empty($percent) && $percent == 100) { |
189
|
|
|
$conn->exec('rm ' . $logPath); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return $percent; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
public function uploadShellScripts(\Twig_Environment $twig) |
196
|
|
|
{ |
197
|
|
|
$conn = $this->getMachine()->getConnection(); |
198
|
|
|
$game = $this->getGame(); |
199
|
|
|
|
200
|
|
|
$scriptPath = $this->getAbsoluteDir() . 'minecraft.sh'; |
201
|
|
|
|
202
|
|
|
$minecraftScript = $twig->render('DPMinecraftServerBundle:sh:minecraft.sh.twig', array( |
203
|
|
|
'screenName' => $this->getScreenName(), 'bin' => $game->getBin(), |
204
|
|
|
'options' => 'nogui', 'minHeap' => $this->getMinHeap(), 'maxHeap' => $this->getMaxHeap(), |
205
|
|
|
'parallelThreads' => 1, 'binDir' => $this->getAbsoluteBinDir(), 'core' => implode(',', $this->getCore()), |
206
|
|
|
)); |
207
|
|
|
|
208
|
|
|
if (!$conn->upload($scriptPath, $minecraftScript, 0750)) { |
209
|
|
|
return false; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
$this->installationStatus = 101; |
213
|
|
|
|
214
|
|
|
return true; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
public function regenerateScripts(\Twig_Environment $twig) |
218
|
|
|
{ |
219
|
|
|
return $this->uploadShellScripts($twig); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* {@inheritdoc} |
224
|
|
|
*/ |
225
|
|
|
public function changeState($state) |
226
|
|
|
{ |
227
|
|
|
$scriptPath = $this->getAbsoluteDir() . 'minecraft.sh'; |
228
|
|
|
|
229
|
|
|
return $this->getMachine()->getConnection()->exec($scriptPath . ' ' . $state); |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
public function uploadDefaultServerConfigurationFile() |
233
|
|
|
{ |
234
|
|
|
$template = $this->getGame()->getConfigTemplate(); |
235
|
|
|
|
236
|
|
|
if (!empty($template)) { |
237
|
|
|
$conn = $this->getMachine()->getConnection(); |
238
|
|
|
$cfgPath = $this->getAbsoluteDir() . 'server.properties'; |
239
|
|
|
|
240
|
|
|
// Supression du fichier s'il existe déjà |
241
|
|
|
$conn->exec('if [ -e ' . $cfgPath . ']; then rm ' . $cfgPath . '; fi'); |
242
|
|
|
|
243
|
|
|
$env = new \Twig_Environment(new \Twig_Loader_String()); |
|
|
|
|
244
|
|
|
$cfgFile = $env->render($template, array( |
245
|
|
|
'serverPort' => $this->getPort(), |
246
|
|
|
'queryPort' => $this->getQueryPort(), |
247
|
|
|
'rconPort' => $this->getRconPort(), |
248
|
|
|
'rconPassword' => $this->getRconPassword(), |
249
|
|
|
'maxPlayers' => $this->getMaxplayers(), |
250
|
|
|
'motd' => $this->getFullName(), |
251
|
|
|
'ip' => $this->getMachine()->getPublicIp(), |
252
|
|
|
)); |
253
|
|
|
|
254
|
|
|
return $conn->upload($cfgPath, $cfgFile, 0750); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
return false; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
public function modifyServerPropertiesFile() |
261
|
|
|
{ |
262
|
|
|
// Variables à modifier dans le fichier server.properties |
263
|
|
|
$varToChange = array( |
264
|
|
|
'motd' => $this->getName(), |
265
|
|
|
'server-port' => $this->getPort(), |
266
|
|
|
'enable-query' => 'true', |
267
|
|
|
'query.port' => $this->getQueryPort(), |
268
|
|
|
'enable-rcon' => 'true', |
269
|
|
|
'rcon.port' => $this->getRconPort(), |
270
|
|
|
'rcon.password' => $this->getRconPassword(), |
271
|
|
|
'server-ip' => $this->getMachine()->getPublicIp(), |
272
|
|
|
'max-players' => $this->getMaxplayers(), |
273
|
|
|
); |
274
|
|
|
|
275
|
|
|
// Récupération du fichier server.properties distant |
276
|
|
|
$conn = $this->getMachine()->getConnection(); |
277
|
|
|
$cfgPath = $this->getAbsoluteDir() . 'server.properties'; |
278
|
|
|
|
279
|
|
|
$remoteFile = $conn->download($cfgPath); |
280
|
|
|
$fileLines = explode("\n", $remoteFile); |
281
|
|
|
|
282
|
|
|
foreach ($fileLines AS &$line) { |
283
|
|
|
if (false === $pos = strpos($line, '=')) continue; |
284
|
|
|
|
285
|
|
|
// Extraction du nom de la variable |
286
|
|
|
$var = substr($line, 0, $pos); |
287
|
|
|
|
288
|
|
|
// Si c'est l'une des variables à modifier, on modifie la ligne |
289
|
|
|
// Et on supprime l'entrée dans l'array des variables à modifier |
290
|
|
|
if (array_key_exists($var, $varToChange)) { |
291
|
|
|
$line = $var . '=' . $varToChange[$var]; |
292
|
|
|
|
293
|
|
|
unset($varToChange[$var]); |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
// Suppression de la référence |
297
|
|
|
unset($line); |
298
|
|
|
|
299
|
|
|
// S'il reste des variables dans l'array $varToChange |
300
|
|
|
// On ajoute les lignes au fichier |
301
|
|
|
// (puisqu'elle n'existe pas, les nouvelles valeurs n'ont pas encore été mises) |
302
|
|
|
if (!empty($varToChange)) { |
303
|
|
|
foreach ($varToChange AS $var => $val) { |
304
|
|
|
$fileLines[] .= $var . '=' . $val; |
305
|
|
|
} |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
// Upload du nouveau fichier |
309
|
|
|
return $conn->upload($cfgPath, implode("\n", $fileLines)); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
public function execPluginScript(\Twig_Environment $twig, Plugin $plugin, $action) |
313
|
|
|
{ |
314
|
|
|
if ($action != 'install' && $action != 'uninstall') { |
315
|
|
|
throw new BadMethodCallException('Only actions available for MinecraftServers plugin script are : install and uninstall.'); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
$conn = $this->getMachine()->getConnection(); |
319
|
|
|
|
320
|
|
|
$dir = $this->getAbsoluteDir(); |
321
|
|
|
$scriptPath = $dir . 'plugin.sh'; |
322
|
|
|
$pluginScript = $twig->render('DPMinecraftServerBundle:sh:plugin.sh.twig', array('gameDir' => $dir . 'plugins')); |
323
|
|
|
|
324
|
|
|
$conn->upload($scriptPath, $pluginScript); |
325
|
|
|
|
326
|
|
|
$screenName = $this->getPluginInstallScreenName(); |
327
|
|
|
$screenCmd = 'screen -dmS ' . $screenName . ' ' . $scriptPath . ' ' . $action; |
328
|
|
|
|
329
|
|
|
if ($action == 'install') { |
330
|
|
|
$screenCmd .= ' "' . $plugin->getDownloadUrl () . '"'; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
$conn->exec($screenCmd); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
View Code Duplication |
public function removeFromServer() |
|
|
|
|
337
|
|
|
{ |
338
|
|
|
$screenName = $this->getScreenName(); |
339
|
|
|
$serverDir = $this->getAbsoluteDir(); |
340
|
|
|
$scriptPath = $serverDir . 'minecraft.sh'; |
341
|
|
|
|
342
|
|
|
$conn = $this->getMachine()->getConnection(); |
343
|
|
|
|
344
|
|
|
// On commence par vérifier que le serveur n'est pas lancé (sinon on l'arrête) |
345
|
|
|
$pgrep = '`ps aux | grep SCREEN | grep "' . $screenName . ' " | grep -v grep | wc -l`'; |
346
|
|
|
$stopCmd = 'if [ ' . $pgrep . ' != "0" ]; then ' . $scriptPath . ' stop; fi;'; |
347
|
|
|
$conn->exec($stopCmd); |
348
|
|
|
|
349
|
|
|
// Puis on supprime complètement le dossier du serveur |
350
|
|
|
return $conn->remove($serverDir); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
public function removeInstallationFiles() |
354
|
|
|
{ |
355
|
|
|
$logPath = $this->getAbsoluteDir() . 'install.log'; |
356
|
|
|
|
357
|
|
|
return $this->getMachine()->getConnection()->remove($logPath); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
public static function loadValidatorMetadata(ClassMetadata $metadata) |
361
|
|
|
{ |
362
|
|
|
$metadata->addPropertyConstraint('queryPort', new Assert\NotBlank(array('message' => 'minecraft.assert.query_port.empty'))); |
363
|
|
|
$metadata->addPropertyConstraint('queryPort', new Assert\Range(array( |
364
|
|
|
'min' => 1024, |
365
|
|
|
'minMessage' => 'minecraft.assert.query_port.min', |
366
|
|
|
'max' => 65536, |
367
|
|
|
'maxMessage' => 'minecraft.assert.query_port.max' |
368
|
|
|
))); |
369
|
|
|
$metadata->addPropertyConstraint('rconPort', new Assert\NotBlank(array('message' => 'minecraft.assert.rcon_port.empty'))); |
370
|
|
|
$metadata->addPropertyConstraint('rconPort', new Assert\Range(array( |
371
|
|
|
'min' => 1024, |
372
|
|
|
'minMessage' => 'minecraft.assert.rcon_port.min', |
373
|
|
|
'max' => 65536, |
374
|
|
|
'maxMessage' => 'minecraft.assert.rcon_port.max' |
375
|
|
|
))); |
376
|
|
|
$metadata->addPropertyConstraint('minHeap', new Assert\NotBlank(array('message' => 'minecraft.assert.min_heap'))); |
377
|
|
|
$metadata->addPropertyConstraint('maxHeap', new Assert\NotBlank(array('message' => 'minecraft.assert.max_heap'))); |
378
|
|
|
} |
379
|
|
|
} |
380
|
|
|
|
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.