supported skin storage for both steve and alex model

This commit is contained in:
printempw 2016-02-05 15:56:17 +08:00
parent 6c8241ee8e
commit 212c0d6767
10 changed files with 229 additions and 144 deletions

View File

@ -3,7 +3,7 @@
* @Author: prpr
* @Date: 2016-02-03 14:39:50
* @Last Modified by: prpr
* @Last Modified time: 2016-02-04 23:45:06
* @Last Modified time: 2016-02-05 15:52:39
*/
session_start();
@ -83,13 +83,14 @@ if (isset($_SESSION['uname'])) {
<td><?php echo $row['uid']; ?></td>
<td><?php echo $row['username']; ?></td>
<td>
<?php echo '<img id="'.$row['username'].'_skin" width="64" '.(($row['skin_hash'] == "") ? '' : 'src="../skin/'.$row['username'].'.png"').'/>'; ?>
<?php echo '<img id="'.$row['username'].'_cape" width="64" '.(($row['cape_hash'] == "") ? '' : 'src="../cape/'.$row['username'].'.png"').'/>'; ?>
<?php echo '<img id="'.$row['username'].'_skin" width="64" '.(($row['hash_steve'] == "") ? '' : 'src="../skin/'.$row['username'].'-steve.png"').'/>'; ?>
<?php echo '<img id="'.$row['username'].'_skin" width="64" '.(($row['hash_alex'] == "") ? '' : 'src="../skin/'.$row['username'].'-alex.png"').'/>'; ?>
<?php echo '<img id="'.$row['username'].'_cape" width="64" '.(($row['hash_cape'] == "") ? '' : 'src="../cape/'.$row['username'].'.png"').'/>'; ?>
</td>
<td>
<a href="javascript:uploadTexture('<?php echo $row['username']; ?>', 'skin');" class="pure-button pure-button-primary">Skin</a>
<a href="javascript:uploadSkin('<?php echo $row['username']; ?>');" class="pure-button pure-button-primary">Skin</a>
<a href="javascript:uploadTexture('<?php echo $row['username']; ?>', 'cape');" class="pure-button pure-button-primary">Cape</a>
<a href="javascript:changeModel('<?php echo $row['username']; ?>', 'cape');" class="pure-button pure-button-default">Model</a>
<a href="javascript:changeModel('<?php echo $row['username']; ?>');" class="pure-button pure-button-default">Model</a>
<span>(<?php echo $row['preference']; ?>)</span>
</td>
<td>

View File

@ -3,7 +3,7 @@
* @Author: printempw
* @Date: 2016-01-16 23:01:33
* @Last Modified by: prpr
* @Last Modified time: 2016-02-04 18:48:30
* @Last Modified time: 2016-02-05 15:35:31
*
* - login, register, logout
* - upload, change, delete
@ -20,7 +20,12 @@ require "$dir/includes/autoload.inc.php";
database::checkConfig();
if (isset($_POST['uname'])) {
$user = new user($_POST['uname']);
$uname = $_POST['uname'];
if (user::checkValidUname($uname)) {
$user = new user($_POST['uname']);
} else {
utils::raise(1, 'Invalid username. Only letters, numbers and _ is allowed.');
}
} else {
utils::raise('1', 'Empty username.');
}
@ -29,7 +34,6 @@ $json = null;
/**
* Handle requests from index.php
* @var [type]
*/
if ($action == "login") {
if (checkPost()) {
@ -59,7 +63,7 @@ if ($action == "login") {
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
// If amout of registered accounts of IP is more than allowed mounts,
// If amount of registered accounts of IP is more than allowed mounts,
// then reject the registration.
if ($user->db->getNumRows('ip', $ip) < REGS_PER_IP) {
// use once md5 to encrypt password
@ -99,7 +103,8 @@ if ($action == "upload") {
if (utils::getValue('token', $_SESSION) == $user->getToken()) {
if (checkFile()) {
if ($file = utils::getValue('skin_file', $_FILES)) {
if ($user->setTexture('skin', $file)) {
$model = (isset($_GET['model']) && $_GET['model'] == "steve") ? "steve" : "alex";
if ($user->setTexture($model, $file)) {
$json['skin']['errno'] = 0;
$json['skin']['msg'] = "Skin uploaded successfully.";
} else {
@ -121,14 +126,15 @@ if ($action == "upload") {
$json['errno'] = 1;
$json['msg'] = "Invalid token.";
}
} else if ($action == "logout") {
if (utils::getValue('token', $_SESSION)) {
session_destroy();
} else if ($action == "model") {
if (utils::getValue('token', $_SESSION) == $user->getToken()) {
$new_model = ($user->getPreference() == "default") ? "slim" : "default";
$user->setPreference($new_model);
$json['errno'] = 0;
$json['msg'] = 'Session destroyed.';
$json['msg'] = "Preferred model successfully changed to ".$user->getPreference().".";
} else {
$json['errno'] = 1;
$json['msg'] = 'No available session.';
$json['msg'] = "Invalid token.";
}
}
@ -221,6 +227,15 @@ if ($action == "change") {
$json['errno'] = 1;
$json['msg'] = "Invalid token.";
}
} else if ($action == "logout") {
if (utils::getValue('token', $_SESSION)) {
session_destroy();
$json['errno'] = 0;
$json['msg'] = 'Session destroyed.';
} else {
$json['errno'] = 1;
$json['msg'] = 'No available session.';
}
}
if (!$action) {

View File

@ -2,7 +2,7 @@
* @Author: prpr
* @Date: 2016-01-21 19:12:06
* @Last Modified by: prpr
* @Last Modified time: 2016-02-05 11:16:53
* @Last Modified time: 2016-02-05 15:13:00
*/
.home-menu-blur {
@ -100,3 +100,6 @@ input[type=radio] {
.container {
position: initial !important;
}
#skinpreview > p {
margin: 20px 0;
}

View File

@ -2,11 +2,23 @@
* @Author: prpr
* @Date: 2016-02-04 16:48:42
* @Last Modified by: prpr
* @Last Modified time: 2016-02-04 18:27:44
* @Last Modified time: 2016-02-05 15:52:32
*/
'use strict';
function uploadSkin(uname) {
Ply.dialog("confirm", {
text: "Which model do you want to change?",
ok: "Steve",
cancel: "Alex"
}).done(function(){
uploadTexture(uname, 'steve');
}).fail(function(){
uploadTexture(uname, 'alex');
});
}
function uploadTexture(uname, type) {
var ply = new Ply({
el: '<h2>Upload new '+type+':</h2>'+
@ -31,7 +43,7 @@ function uploadTexture(uname, type) {
location.reload();
});
} else {
showAlert("Error when uploading cape:\n" + json.msg);
showAlert("Error when uploading texture:\n" + json.msg);
}
}
});

View File

@ -2,115 +2,134 @@
* @Author: prpr
* @Date: 2016-01-21 13:56:40
* @Last Modified by: prpr
* @Last Modified time: 2016-02-05 11:38:39
* @Last Modified time: 2016-02-05 15:35:02
*/
'use strict';
$("body").on("change", "#skininput", function(){
var files = $("#skininput").prop("files");
handleFiles(files, "skin");
$('body').on('change', '#skininput', function(){
var files = $('#skininput').prop('files');
handleFiles(files, 'skin');
});
$("body").on("change", "#capeinput", function(){
var files = $("#capeinput").prop("files");
handleFiles(files, "cape");
$('body').on('change', '#capeinput', function(){
var files = $('#capeinput').prop('files');
handleFiles(files, 'cape');
});
// Real-time preview
function handleFiles(files, type) {
if(files.length > 0) {
var file = files[0];
if(file.type === 'image/png') {
var fr = new FileReader();
fr.onload = function (e) {
var img = new Image();
img.onload = function () {
if (type == "skin") {
MSP.changeSkin(img.src);
} else {
MSP.changeCape(img.src);
}
};
img.onerror = function () {
showMsg("alert-danger", "Error: Not an image or unknown file format");
};
img.src = this.result;
};
fr.readAsDataURL(file);
} else {
showMsg("alert-danger", "Error: This is not a PNG image!");
}
}
if(files.length > 0) {
var file = files[0];
if(file.type === "image/png") {
var fr = new FileReader();
fr.onload = function (e) {
var img = new Image();
img.onload = function () {
if (type == "skin") {
MSP.changeSkin(img.src);
} else {
MSP.changeCape(img.src);
}
};
img.onerror = function () {
showMsg("alert-danger", "Error: Not an image or unknown file format");
};
img.src = this.result;
};
fr.readAsDataURL(file);
} else {
showMsg("alert-danger", "Error: This is not a PNG image!");
}
}
};
function init3dCanvas() {
if ($(window).width() < 800) {
var canvas = MSP.get3dSkinCanvas($('#skinpreview').width(), $('#skinpreview').width());
$("#skinpreview").append($(canvas).prop("id", "canvas3d"));
} else {
var canvas = MSP.get3dSkinCanvas(400, 400);
$("#skinpreview").append($(canvas).prop("id", "canvas3d"));
}
if ($(window).width() < 800) {
var canvas = MSP.get3dSkinCanvas($('#skinpreview').width(), $('#skinpreview').width());
$("#skinpreview").append($(canvas).prop("id", "canvas3d"));
} else {
var canvas = MSP.get3dSkinCanvas(400, 400);
$("#skinpreview").append($(canvas).prop("id", "canvas3d"));
}
}
$(document).ready(function(){
init3dCanvas();
});
$(window).resize(function(){
init3dCanvas();
});
$(document).ready(init3dCanvas);
// Auto resize canvas to fit responsive design
$(window).resize(init3dCanvas);
// Change 3D preview status
$("[title='Movements']").click(function(){
MSP.setStatus("movements", !MSP.getStatus("movements"));
MSP.setStatus("movements", !MSP.getStatus("movements"));
});
$("[title='Running']").click(function(){
MSP.setStatus("running", !MSP.getStatus("running"));
MSP.setStatus("running", !MSP.getStatus("running"));
});
$("[title='Rotation']").click(function(){
MSP.setStatus("rotation", !MSP.getStatus("rotation"));
MSP.setStatus("rotation", !MSP.getStatus("rotation"));
});
function show2dPreview() {
$('#canvas3d').remove();
$("#skinpreview").html($('<p>Skin for Steve model:</p>').append($('<img />').css('float', 'right').attr('src', '../skin/admin.png')));
$('#canvas3d').remove();
$("#skinpreview").html($('<p>Skin for Steve model:</p>').append($('<img />').css('float', 'right').attr('src', '../skin/'+docCookies.getItem('uname')+'-steve.png')));
$("#skinpreview").append($('<p>Skin for Alex model:</p>').append($('<img />').css('float', 'right').attr('src', '../skin/'+docCookies.getItem('uname')+'-alex.png')));
$("#skinpreview").append($('<p>Cape:</p>').append($('<img />').css('float', 'right').attr('src', '../cape/'+docCookies.getItem('uname')+'.png')));
}
$("#upload").click(function(){
var skin_file = $("#skininput").get(0).files[0];
var cape_file = $("#capeinput").get(0).files[0];
var form_data = new FormData();
if (skin_file) form_data.append('skin_file', skin_file);
if (cape_file) form_data.append('cape_file', cape_file);
if (skin_file || cape_file) {
$.ajax({
type: 'POST',
url: '../ajax.php?action=upload',
contentType: false,
dataType: "json",
data: form_data,
processData: false,
beforeSend: function() {
showMsg("alert-info", "Uploading...");
},
success: function(json) {
console.log(json);
if (json.skin.errno == 0 && json.cape.errno == 0) {
showMsg("alert-success", "Successfully uploaded.");
}
if (json.skin.errno != 0) {
showMsg("alert-danger", "Error when uploading skin:\n"+json.skin.msg);
}
if (json.cape.errno != 0) {
showMsg("alert-danger", "Error when uploading cape:\n"+json.cape.msg);
}
}
});
} else {
showMsg("alert-warning", "No input file selected");
}
var model = $('#model-steve').prop('checked') ? "steve" : "alex";
var skin_file = $('#skininput').get(0).files[0];
var cape_file = $('#capeinput').get(0).files[0];
var form_data = new FormData();
if (skin_file) form_data.append('skin_file', skin_file);
if (cape_file) form_data.append('cape_file', cape_file);
form_data.append('uname', docCookies.getItem('uname'));
// Ajax file upload
if (skin_file || cape_file) {
$.ajax({
type: 'POST',
url: '../ajax.php?action=upload&model='+model,
contentType: false,
dataType: "json",
data: form_data,
processData: false,
beforeSend: function() {
showMsg('alert-info', 'Uploading...');
},
success: function(json) {
console.log(json);
if (json.skin.errno == 0 && json.cape.errno == 0) {
showMsg('alert-success', 'Successfully uploaded.');
}
if (json.skin.errno != 0) {
showMsg('alert-danger', 'Error when uploading skin:\n'+json.skin.msg);
}
if (json.cape.errno != 0) {
showMsg('alert-danger', 'Error when uploading cape:\n'+json.cape.msg);
}
}
});
} else {
showMsg('alert-warning', 'No input file selected');
}
});
function changeModel(uname) {
showAlert("Sure to change?", function(){
$.ajax({
type: "POST",
url: "../ajax.php?action=model",
data: { "uname": docCookies.getItem('uname') },
dataType: "json",
success: function(json) {
if (json.errno == 0) {
showAlert(json.msg, function(){
location.reload();
});
} else {
showAlert(json.msg);
}
}
});
});
}

View File

@ -3,7 +3,7 @@
* @Author: prpr
* @Date: 2016-02-02 20:56:42
* @Last Modified by: prpr
* @Last Modified time: 2016-02-04 22:06:20
* @Last Modified time: 2016-02-05 14:51:26
*
* All textures requests of legacy link will be handle here.
*/
@ -18,10 +18,15 @@ if (isset($_GET['type']) && isset($_GET['uname'])) {
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : null;
if ($_GET['type'] == "skin" || $_GET['type'] == "cape") {
$model = (isset($_GET['model']) && $_GET['model'] == "steve") ? "steve" : "alex";
if ($if_modified_since >= $user->getLastModified()) {
header('HTTP/1.0 304 Not Modified');
} else {
echo $user->getBinaryTexture($_GET['type']);
if ($_GET['type'] == "cape") {
echo $user->getBinaryTexture('cape');
} else {
echo $user->getBinaryTexture($model);
}
}
} else if ($_GET['type'] == "json") {
if (isset($_GET['api'])) {

View File

@ -3,7 +3,7 @@
* @Author: printempw
* @Date: 2016-01-16 23:01:33
* @Last Modified by: prpr
* @Last Modified time: 2016-02-04 20:38:42
* @Last Modified time: 2016-02-05 15:11:35
*/
class user
@ -37,6 +37,10 @@ class user
}
}
public static function checkValidUname($uname) {
return preg_match("([A-Za-z0-9_\-]+)", $uname);
}
public static function checkValidPwd($passwd) {
if (strlen($passwd) > 16 || strlen($passwd) < 5) {
utils::raise(1, 'Illegal password. Password length should be in 5~16.');
@ -55,32 +59,33 @@ class user
}
public function register($passwd, $ip) {
if ($this->db->insert(array(
"uname" => $this->uname,
"passwd" => $passwd,
"ip" => $ip
)))
{
return true;
} else {
return false;
}
return $this->db->insert(array(
"uname" => $this->uname,
"passwd" => $passwd,
"ip" => $ip
));
}
public function unRegister() {
if ($this->getTexture('skin') != "")
utils::remove("./textures/".$this->getTexture('skin'));
if ($this->getTexture('skin') != "")
if ($this->getTexture('steve') != "")
utils::remove("./textures/".$this->getTexture('steve'));
if ($this->getTexture('alex') != "")
utils::remove("./textures/".$this->getTexture('alex'));
if ($this->getTexture('cape') != "")
utils::remove("./textures/".$this->getTexture('cape'));
return $this->db->delete($this->uname);
}
/**
* Get textures of user
* @param string $type steve|alex|cape
* @return string sha256-hash of texture file
*/
public function getTexture($type) {
if ($type == "skin") {
return $this->db->select('username', $this->uname)['skin_hash'];
} else if ($type == "cape") {
return $this->db->select('username', $this->uname)['cape_hash'];
}
if ($type == "skin")
$type = ($this->getPreference() == "default") ? "steve" : "alex";
if ($type == "steve" | $type == "alex" | $type == "cape")
return $this->db->select('username', $this->uname)['hash_'.$type];
return false;
}
@ -88,9 +93,9 @@ class user
$filename = "./textures/".$this->getTexture($type);
if (file_exists($filename)) {
header('Content-Type: image/png');
// Cache friendly
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $this->getLastModified()).' GMT');
$data = fread(fopen($filename, 'r'), filesize($filename));
return $data;
return utils::fread($filename);
} else {
utils::raise(-1, 'Texture no longer exists.');
}
@ -98,21 +103,19 @@ class user
public function setTexture($type, $file) {
$hash = utils::upload($file);
if ($type == "skin") {
// remove the original texture first
if ($this->getTexture('skin') != "")
utils::remove("./textures/".$this->getTexture('skin'));
$this->updateLastModified();
return $this->db->update($this->uname, 'skin_hash', $hash);
} else if ($type == "cape") {
if ($this->getTexture('cape') != "")
utils::remove("./textures/".$this->getTexture('cape'));
$this->updateLastModified();
return $this->db->update($this->uname, 'cape_hash', $hash);
}
// Remove the original texture first
if ($this->getTexture($type) != "")
utils::remove("./textures/".$this->getTexture($type));
$this->updateLastModified();
if ($type == "steve" | $type == "alex" | $type == "cape")
return $this->db->update($this->uname, 'hash_'.$type, $hash);
return false;
}
/**
* Set preferred model
* @param string $type, 'slim' or 'default'
*/
public function setPreference($type) {
return $this->db->update($this->uname, 'preference', $type);
}
@ -121,9 +124,15 @@ class user
return $this->db->select('username', $this->uname)['preference'];
}
/**
* Get JSON profile
* @param int $api_type, which API to use, 0 for CustomSkinAPI, 1 for UniSkinAPI
* @return string, user profile in json format
*/
public function getJsonProfile($api_type) {
header('Content-type: application/json');
if ($this->is_registered) {
// Support both CustomSkinLoader API & UniSkinAPI
if ($api_type == 0 || $api_type == 1) {
$json[($api_type == 0) ? 'username' : 'player_name'] = $this->uname;
$model = $this->getPreference();
@ -132,8 +141,9 @@ class user
$json['last_update'] = $this->getLastModified();
$json['model_preference'] = [$model, $sec_model];
}
$json['skins'][$model] = $this->getTexture('skin');
$json['skins'][$sec_model] = $this->getTexture('skin');
// Skins dict order by preference model
$json['skins'][$model] = $this->getTexture($model == "default" ? "steve" : "alex");
$json['skins'][$sec_model] = $this->getTexture($sec_model == "default" ? "steve" : "alex");
$json['cape'] = $this->getTexture('cape');
} else {
utils::raise(-1, 'Configuration error. Non-supported API_TYPE.');

View File

@ -3,7 +3,7 @@
* @Author: printempw
* @Date: 2016-01-16 23:01:33
* @Last Modified by: prpr
* @Last Modified time: 2016-02-04 23:43:07
* @Last Modified time: 2016-02-05 13:27:43
*/
class utils
@ -36,6 +36,16 @@ class utils
return $hash;
}
/**
* Read a file and return bin data
*
* @param string $filename
* @return resource, binary data
*/
public static function fread($filename) {
return fread(fopen($filename, 'r'), filesize($filename));
}
/**
* Remove a file
*

View File

@ -71,9 +71,9 @@ if (isset($_SESSION['uname'])) {
<p>Select a cape:</p>
<input type="file" id="capeinput" name="capeinput" accept="image/png" />
<br />
<input type="radio" id="model" name="model" />My skin fits on the classic Steve player model.
<input type="radio" id="model-steve" name="model" /><label for="model-steve">My skin fits on the classic Steve player model.</label>
<br />
<input type="radio" id="model" name="model" />My skin fits on the new Alex player model.
<input type="radio" id="model-alex" name="model" /><label for="model-alex">My skin fits on the new Alex player model.</label>
<br /><br />
<button id="upload" class="pure-button pure-button-primary">Upload</button>
<a href="javascript:show2dPreview();" class="pure-button">2D Preview</a>
@ -81,6 +81,12 @@ if (isset($_SESSION['uname'])) {
<div id="msg" class="alert hide" role="alert"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Change Preferred Model</div>
<div class="panel-body">
<p>Your preference model is <b><?php echo $user->getPreference(); ?></b>. <a class="pure-button pure-button-default" style="margin: 0 3px;" href="javascript:changeModel();">Change?</a></p>
</div>
</div>
</div>
<div class="pure-u-md-1-2 pure-u-1">
<div class="panel panel-default">
@ -104,5 +110,9 @@ if (isset($_SESSION['uname'])) {
<script type="text/javascript" src="../libs/cookie.js"></script>
<script type="text/javascript" src="../assets/js/utils.js"></script>
<script type="text/javascript" src="../assets/js/user.utils.js"></script>
<?php if ($user->getTexture('alex') && ($user->getTexture('steve') == "")) {?>
<script type="text/javascript">
showMsg('alert-warning', 'It seems that you have only uploaded skin for Alex model. Note that Minecraft version < 1.8 does not support Alex model. Uploading external skin fit on Steve model is recommemded.');
</script>
<?php } ?>
</html>

View File

@ -3,7 +3,7 @@
* @Author: prpr
* @Date: 2016-02-04 19:37:21
* @Last Modified by: prpr
* @Last Modified time: 2016-02-04 22:43:15
* @Last Modified time: 2016-02-05 15:02:52
*/
?>