/** * REST API: WP_REST_Server class * * @package WordPress * @subpackage REST_API * @since 4.4.0 */ /** * Core class used to implement the WordPress REST API server. * * @since 4.4.0 */ #[AllowDynamicProperties] class WP_REST_Server { /** * Alias for GET transport method. * * @since 4.4.0 * @var string */ const READABLE = 'GET'; /** * Alias for POST transport method. * * @since 4.4.0 * @var string */ const CREATABLE = 'POST'; /** * Alias for POST, PUT, PATCH transport methods together. * * @since 4.4.0 * @var string */ const EDITABLE = 'POST, PUT, PATCH'; /** * Alias for DELETE transport method. * * @since 4.4.0 * @var string */ const DELETABLE = 'DELETE'; /** * Alias for GET, POST, PUT, PATCH & DELETE transport methods together. * * @since 4.4.0 * @var string */ const ALLMETHODS = 'GET, POST, PUT, PATCH, DELETE'; /** * Namespaces registered to the server. * * @since 4.4.0 * @var array */ protected $namespaces = array(); /** * Endpoints registered to the server. * * @since 4.4.0 * @var array */ protected $endpoints = array(); /** * Options defined for the routes. * * @since 4.4.0 * @var array */ protected $route_options = array(); /** * Caches embedded requests. * * @since 5.4.0 * @var array */ protected $embed_cache = array(); /** * Stores request objects that are currently being handled. * * @since 6.5.0 * @var array */ protected $dispatching_requests = array(); /** * Instantiates the REST server. * * @since 4.4.0 */ public function __construct() { $this->endpoints = array( // Meta endpoints. '/' => array( 'callback' => array( $this, 'get_index' ), 'methods' => 'GET', 'args' => array( 'context' => array( 'default' => 'view', ), ), ), '/batch/v1' => array( 'callback' => array( $this, 'serve_batch_request_v1' ), 'methods' => 'POST', 'args' => array( 'validation' => array( 'type' => 'string', 'enum' => array( 'require-all-validate', 'normal' ), 'default' => 'normal', ), 'requests' => array( 'required' => true, 'type' => 'array', 'maxItems' => $this->get_max_batch_size(), 'items' => array( 'type' => 'object', 'properties' => array( 'method' => array( 'type' => 'string', 'enum' => array( 'POST', 'PUT', 'PATCH', 'DELETE' ), 'default' => 'POST', ), 'path' => array( 'type' => 'string', 'required' => true, ), 'body' => array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => true, ), 'headers' => array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => array( 'type' => array( 'string', 'array' ), 'items' => array( 'type' => 'string', ), ), ), ), ), ), ), ), ); } /** * Checks the authentication headers if supplied. * * @since 4.4.0 * * @return WP_Error|null|true WP_Error if authentication error occurred, null if authentication * method wasn't used, true if authentication succeeded. */ public function check_authentication() { /** * Filters REST API authentication errors. * * This is used to pass a WP_Error from an authentication method back to * the API. * * Authentication methods should check first if they're being used, as * multiple authentication methods can be enabled on a site (cookies, * HTTP basic auth, OAuth). If the authentication method hooked in is * not actually being attempted, null should be returned to indicate * another authentication method should check instead. Similarly, * callbacks should ensure the value is `null` before checking for * errors. * * A WP_Error instance can be returned if an error occurs, and this should * match the format used by API methods internally (that is, the `status` * data should be used). A callback can return `true` to indicate that * the authentication method was used, and it succeeded. * * @since 4.4.0 * * @param WP_Error|null|true $errors WP_Error if authentication error occurred, null if authentication * method wasn't used, true if authentication succeeded. */ return apply_filters( 'rest_authentication_errors', null ); } /** * Converts an error to a response object. * * This iterates over all error codes and messages to change it into a flat * array. This enables simpler client behavior, as it is represented as a * list in JSON rather than an object/map. * * @since 4.4.0 * @since 5.7.0 Converted to a wrapper of {@see rest_convert_error_to_response()}. * * @param WP_Error $error WP_Error instance. * @return WP_REST_Response List of associative arrays with code and message keys. */ protected function error_to_response( $error ) { return rest_convert_error_to_response( $error ); } /** * Retrieves an appropriate error representation in JSON. * * Note: This should only be used in WP_REST_Server::serve_request(), as it * cannot handle WP_Error internally. All callbacks and other internal methods * should instead return a WP_Error with the data set to an array that includes * a 'status' key, with the value being the HTTP status to send. * * @since 4.4.0 * * @param string $code WP_Error-style code. * @param string $message Human-readable message. * @param int|null $status Optional. HTTP status code to send. Default null. * @return string JSON representation of the error. */ protected function json_error( $code, $message, $status = null ) { if ( $status ) { $this->set_status( $status ); } $error = compact( 'code', 'message' ); return wp_json_encode( $error ); } /** * Gets the encoding options passed to {@see wp_json_encode}. * * @since 6.1.0 * * @param \WP_REST_Request $request The current request object. * * @return int The JSON encode options. */ protected function get_json_encode_options( WP_REST_Request $request ) { $options = 0; if ( $request->has_param( '_pretty' ) ) { $options |= JSON_PRETTY_PRINT; } /** * Filters the JSON encoding options used to send the REST API response. * * @since 6.1.0 * * @param int $options JSON encoding options {@see json_encode()}. * @param WP_REST_Request $request Current request object. */ return apply_filters( 'rest_json_encode_options', $options, $request ); } /** * Handles serving a REST API request. * * Matches the current server URI to a route and runs the first matching * callback then outputs a JSON representation of the returned value. * * @since 4.4.0 * * @see WP_REST_Server::dispatch() * * @global WP_User $current_user The currently authenticated user. * * @param string|null $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used. * Default null. * @return null|false Null if not served and a HEAD request, false otherwise. */ public function serve_request( $path = null ) { // Refuse to start a fresh top-level REST cycle while another dispatch // is already in flight. Internal sub-requests must use dispatch(). if ( $this->is_dispatching() ) { return false; } /* @var WP_User|null $current_user */ global $current_user; if ( $current_user instanceof WP_User && ! $current_user->exists() ) { /* * If there is no current user authenticated via other means, clear * the cached lack of user, so that an authenticate check can set it * properly. * * This is done because for authentications such as Application * Passwords, we don't want it to be accepted unless the current HTTP * request is a REST API request, which can't always be identified early * enough in evaluation. */ $current_user = null; } /** * Filters whether JSONP is enabled for the REST API. * * @since 4.4.0 * * @param bool $jsonp_enabled Whether JSONP is enabled. Default true. */ $jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true ); $jsonp_callback = false; if ( isset( $_GET['_jsonp'] ) ) { $jsonp_callback = $_GET['_jsonp']; } $content_type = ( $jsonp_callback && $jsonp_enabled ) ? 'application/javascript' : 'application/json'; $this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) ); $this->send_header( 'X-Robots-Tag', 'noindex' ); $api_root = get_rest_url(); if ( ! empty( $api_root ) ) { $this->send_header( 'Link', '<' . sanitize_url( $api_root ) . '>; rel="https://api.w.org/"' ); } /* * Mitigate possible JSONP Flash attacks. * * https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ */ $this->send_header( 'X-Content-Type-Options', 'nosniff' ); /** * Filters whether the REST API is enabled. * * @since 4.4.0 * @deprecated 4.7.0 Use the {@see 'rest_authentication_errors'} filter to * restrict access to the REST API. * * @param bool $rest_enabled Whether the REST API is enabled. Default true. */ apply_filters_deprecated( 'rest_enabled', array( true ), '4.7.0', 'rest_authentication_errors', sprintf( /* translators: %s: rest_authentication_errors */ __( 'The REST API can no longer be completely disabled, the %s filter can be used to restrict access to the API, instead.' ), 'rest_authentication_errors' ) ); if ( $jsonp_callback ) { if ( ! $jsonp_enabled ) { echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 ); return false; } if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) { echo $this->json_error( 'rest_callback_invalid', __( 'Invalid JSONP callback function.' ), 400 ); return false; } } if ( empty( $path ) ) { $path = $_SERVER['PATH_INFO'] ?? '/'; } $request = new WP_REST_Request( $_SERVER['REQUEST_METHOD'], $path ); $request->set_query_params( wp_unslash( $_GET ) ); $request->set_body_params( wp_unslash( $_POST ) ); $request->set_file_params( $_FILES ); $request->set_headers( $this->get_headers( wp_unslash( $_SERVER ) ) ); $request->set_body( self::get_raw_data() ); /* * HTTP method override for clients that can't use PUT/PATCH/DELETE. First, we check * $_GET['_method']. If that is not set, we check for the HTTP_X_HTTP_METHOD_OVERRIDE * header. */ $method_overridden = false; if ( isset( $_GET['_method'] ) ) { $request->set_method( $_GET['_method'] ); } elseif ( isset( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ) { $request->set_method( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ); $method_overridden = true; } $expose_headers = array( 'X-WP-Total', 'X-WP-TotalPages', 'Link' ); /** * Filters the list of response headers that are exposed to REST API CORS requests. * * @since 5.5.0 * @since 6.3.0 The `$request` parameter was added. * * @param string[] $expose_headers The list of response headers to expose. * @param WP_REST_Request $request The request in context. */ $expose_headers = apply_filters( 'rest_exposed_cors_headers', $expose_headers, $request ); $this->send_header( 'Access-Control-Expose-Headers', implode( ', ', $expose_headers ) ); $allow_headers = array( 'Authorization', 'X-WP-Nonce', 'Content-Disposition', 'Content-MD5', 'Content-Type', ); /** * Filters the list of request headers that are allowed for REST API CORS requests. * * The allowed headers are passed to the browser to specify which * headers can be passed to the REST API. By default, we allow the * Content-* headers needed to upload files to the media endpoints. * As well as the Authorization and Nonce headers for allowing authentication. * * @since 5.5.0 * @since 6.3.0 The `$request` parameter was added. * * @param string[] $allow_headers The list of request headers to allow. * @param WP_REST_Request $request The request in context. */ $allow_headers = apply_filters( 'rest_allowed_cors_headers', $allow_headers, $request ); $this->send_header( 'Access-Control-Allow-Headers', implode( ', ', $allow_headers ) ); $result = $this->check_authentication(); if ( ! is_wp_error( $result ) ) { $result = $this->dispatch( $request ); } // Normalize to either WP_Error or WP_REST_Response... $result = rest_ensure_response( $result ); // ...then convert WP_Error across. if ( is_wp_error( $result ) ) { $result = $this->error_to_response( $result ); } /** * Filters the REST API response. * * Allows modification of the response before returning. * * @since 4.4.0 * @since 4.5.0 Applied to embedded responses. * * @param WP_HTTP_Response $result Result to send to the client. Usually a `WP_REST_Response`. * @param WP_REST_Server $server Server instance. * @param WP_REST_Request $request Request used to generate the response. */ $result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $request ); // Wrap the response in an envelope if asked for. if ( isset( $_GET['_envelope'] ) ) { $embed = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false; $result = $this->envelope_response( $result, $embed ); } // Send extra data from response objects. $headers = $result->get_headers(); $this->send_headers( $headers ); $code = $result->get_status(); $this->set_status( $code ); /** * Filters whether to send no-cache headers on a REST API request. * * @since 4.4.0 * @since 6.3.2 Moved the block to catch the filter added on rest_cookie_check_errors() from wp-includes/rest-api.php. * * @param bool $rest_send_nocache_headers Whether to send no-cache headers. */ $send_no_cache_headers = apply_filters( 'rest_send_nocache_headers', is_user_logged_in() ); /* * Send no-cache headers if $send_no_cache_headers is true, * OR if the HTTP_X_HTTP_METHOD_OVERRIDE is used but resulted a 4xx response code. */ if ( $send_no_cache_headers || ( true === $method_overridden && str_starts_with( $code, '4' ) ) ) { foreach ( wp_get_nocache_headers() as $header => $header_value ) { if ( empty( $header_value ) ) { $this->remove_header( $header ); } else { $this->send_header( $header, $header_value ); } } } /** * Filters whether the REST API request has already been served. * * Allow sending the request manually - by returning true, the API result * will not be sent to the client. * * @since 4.4.0 * * @param bool $served Whether the request has already been served. Default false. * @param WP_HTTP_Response $result Result to send to the client. Usually a `WP_REST_Response`. * @param WP_REST_Request $request Request used to generate the response. * @param WP_REST_Server $server Server instance. */ $served = apply_filters( 'rest_pre_serve_request', false, $result, $request, $this ); if ( ! $served ) { if ( 'HEAD' === $request->get_method() ) { return null; } // Embed links inside the request. $embed = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false; $result = $this->response_to_data( $result, $embed ); /** * Filters the REST API response. * * Allows modification of the response data after inserting * embedded data (if any) and before echoing the response data. * * @since 4.8.1 * * @param array $result Response data to send to the client. * @param WP_REST_Server $server Server instance. * @param WP_REST_Request $request Request used to generate the response. */ $result = apply_filters( 'rest_pre_echo_response', $result, $this, $request ); // The 204 response shouldn't have a body. if ( 204 === $code || null === $result ) { return null; } $result = wp_json_encode( $result, $this->get_json_encode_options( $request ) ); $json_error_message = $this->get_json_last_error(); if ( $json_error_message ) { $this->set_status( 500 ); $json_error_obj = new WP_Error( 'rest_encode_error', $json_error_message, array( 'status' => 500 ) ); $result = $this->error_to_response( $json_error_obj ); $result = wp_json_encode( $result->data, $this->get_json_encode_options( $request ) ); } if ( $jsonp_callback ) { // Prepend '/**/' to mitigate possible JSONP Flash attacks. // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ echo '/**/' . $jsonp_callback . '(' . $result . ')'; } else { echo $result; } } return null; } /** * Converts a response to data to send. * * @since 4.4.0 * @since 5.4.0 The `$embed` parameter can now contain a list of link relations to include. * * @param WP_REST_Response $response Response object. * @param bool|string[] $embed Whether to embed all links, a filtered list of link relations, or no links. * @return array { * Data with sub-requests embedded. * * @type array $_links Links. * @type array $_embedded Embedded objects. * } */ public function response_to_data( $response, $embed ) { $data = $response->get_data(); $links = self::get_compact_response_links( $response ); if ( ! empty( $links ) ) { // Convert links to part of the data. $data['_links'] = $links; } if ( $embed ) { $this->embed_cache = array(); // Determine if this is a numeric array. if ( wp_is_numeric_array( $data ) ) { foreach ( $data as $key => $item ) { $data[ $key ] = $this->embed_links( $item, $embed ); } } else { $data = $this->embed_links( $data, $embed ); } $this->embed_cache = array(); } return $data; } /** * Retrieves links from a response. * * Extracts the links from a response into a structured hash, suitable for * direct output. * * @since 4.4.0 * * @param WP_REST_Response $response Response to extract links from. * @return array Map of link relation to list of link hashes. */ public static function get_response_links( $response ) { $links = $response->get_links(); if ( empty( $links ) ) { return array(); } // Convert links to part of the data. $data = array(); foreach ( $links as $rel => $items ) { $data[ $rel ] = array(); foreach ( $items as $item ) { $attributes = $item['attributes']; $attributes['href'] = $item['href']; if ( 'self' !== $rel ) { $data[ $rel ][] = $attributes; continue; } $target_hints = self::get_target_hints_for_link( $attributes ); if ( $target_hints ) { $attributes['targetHints'] = $target_hints; } $data[ $rel ][] = $attributes; } } return $data; } /** * Gets the target hints for a REST API Link. * * @since 6.7.0 * * @param array $link The link to get target hints for. * @return array|null */ protected static function get_target_hints_for_link( $link ) { // Prefer targetHints that were specifically designated by the developer. if ( isset( $link['targetHints']['allow'] ) ) { return null; } $request = WP_REST_Request::from_url( $link['href'] ); if ( ! $request ) { return null; } $server = rest_get_server(); $match = $server->match_request_to_handler( $request ); if ( is_wp_error( $match ) ) { return null; } if ( is_wp_error( $request->has_valid_params() ) ) { return null; } if ( is_wp_error( $request->sanitize_params() ) ) { return null; } $target_hints = array(); $response = new WP_REST_Response(); $response->set_matched_route( $match[0] ); $response->set_matched_handler( $match[1] ); $headers = rest_send_allow_header( $response, $server, $request )->get_headers(); foreach ( $headers as $name => $value ) { $name = WP_REST_Request::canonicalize_header_name( $name ); $target_hints[ $name ] = array_map( 'trim', explode( ',', $value ) ); } return $target_hints; } /** * Retrieves the CURIEs (compact URIs) used for relations. * * Extracts the links from a response into a structured hash, suitable for * direct output. * * @since 4.5.0 * * @param WP_REST_Response $response Response to extract links from. * @return array Map of link relation to list of link hashes. */ public static function get_compact_response_links( $response ) { $links = self::get_response_links( $response ); if ( empty( $links ) ) { return array(); } $curies = $response->get_curies(); $used_curies = array(); foreach ( $links as $rel => $items ) { // Convert $rel URIs to their compact versions if they exist. foreach ( $curies as $curie ) { $href_prefix = substr( $curie['href'], 0, strpos( $curie['href'], '{rel}' ) ); if ( ! str_starts_with( $rel, $href_prefix ) ) { continue; } // Relation now changes from '$uri' to '$curie:$relation'. $rel_regex = str_replace( '\{rel\}', '(.+)', preg_quote( $curie['href'], '!' ) ); preg_match( '!' . $rel_regex . '!', $rel, $matches ); if ( $matches ) { $new_rel = $curie['name'] . ':' . $matches[1]; $used_curies[ $curie['name'] ] = $curie; $links[ $new_rel ] = $items; unset( $links[ $rel ] ); break; } } } // Push the curies onto the start of the links array. if ( $used_curies ) { $links['curies'] = array_values( $used_curies ); } return $links; } /** * Embeds the links from the data into the request. * * @since 4.4.0 * @since 5.4.0 The `$embed` parameter can now contain a list of link relations to include. * * @param array $data Data from the request. * @param bool|string[] $embed Whether to embed all links or a filtered list of link relations. * Default true. * @return array { * Data with sub-requests embedded. * * @type array $_links Links. * @type array $_embedded Embedded objects. * } */ protected function embed_links( $data, $embed = true ) { if ( empty( $data['_links'] ) ) { return $data; } $embedded = array(); foreach ( $data['_links'] as $rel => $links ) { /* * If a list of relations was specified, and the link relation * is not in the list of allowed relations, don't process the link. */ if ( is_array( $embed ) && ! in_array( $rel, $embed, true ) ) { continue; } $embeds = array(); foreach ( $links as $item ) { // Determine if the link is embeddable. if ( empty( $item['embeddable'] ) ) { // Ensure we keep the same order. $embeds[] = array(); continue; } if ( ! array_key_exists( $item['href'], $this->embed_cache ) ) { // Run through our internal routing and serve. $request = WP_REST_Request::from_url( $item['href'] ); if ( ! $request ) { $embeds[] = array(); continue; } // Embedded resources get passed context=embed. if ( empty( $request['context'] ) ) { $request['context'] = 'embed'; } if ( empty( $request['per_page'] ) ) { $matched = $this->match_request_to_handler( $request ); if ( ! is_wp_error( $matched ) && isset( $matched[1]['args']['per_page']['maximum'] ) ) { $request['per_page'] = (int) $matched[1]['args']['per_page']['maximum']; } } $response = $this->dispatch( $request ); /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ $response = apply_filters( 'rest_post_dispatch', rest_ensure_response( $response ), $this, $request ); $this->embed_cache[ $item['href'] ] = $this->response_to_data( $response, false ); } $embeds[] = $this->embed_cache[ $item['href'] ]; } // Determine if any real links were found. $has_links = count( array_filter( $embeds ) ); if ( $has_links ) { $embedded[ $rel ] = $embeds; } } if ( ! empty( $embedded ) ) { $data['_embedded'] = $embedded; } return $data; } /** * Wraps the response in an envelope. * * The enveloping technique is used to work around browser/client * compatibility issues. Essentially, it converts the full HTTP response to * data instead. * * @since 4.4.0 * @since 6.0.0 The `$embed` parameter can now contain a list of link relations to include. * * @param WP_REST_Response $response Response object. * @param bool|string[] $embed Whether to embed all links, a filtered list of link relations, or no links. * @return WP_REST_Response New response with wrapped data */ public function envelope_response( $response, $embed ) { $envelope = array( 'body' => $this->response_to_data( $response, $embed ), 'status' => $response->get_status(), 'headers' => $response->get_headers(), ); /** * Filters the enveloped form of a REST API response. * * @since 4.4.0 * * @param array $envelope { * Envelope data. * * @type array $body Response data. * @type int $status The 3-digit HTTP status code. * @type array $headers Map of header name to header value. * } * @param WP_REST_Response $response Original response data. */ $envelope = apply_filters( 'rest_envelope_response', $envelope, $response ); // Ensure it's still a response and return. return rest_ensure_response( $envelope ); } /** * Registers a route to the server. * * @since 4.4.0 * * @param string $route_namespace Namespace. * @param string $route The REST route. * @param array $route_args Route arguments. * @param bool $override Optional. Whether the route should be overridden if it already exists. * Default false. */ public function register_route( $route_namespace, $route, $route_args, $override = false ) { if ( ! isset( $this->namespaces[ $route_namespace ] ) ) { $this->namespaces[ $route_namespace ] = array(); $this->register_route( $route_namespace, '/' . $route_namespace, array( array( 'methods' => self::READABLE, 'callback' => array( $this, 'get_namespace_index' ), 'args' => array( 'namespace' => array( 'default' => $route_namespace, ), 'context' => array( 'default' => 'view', ), ), ), ) ); } // Associative to avoid double-registration. $this->namespaces[ $route_namespace ][ $route ] = true; $route_args['namespace'] = $route_namespace; if ( $override || empty( $this->endpoints[ $route ] ) ) { $this->endpoints[ $route ] = $route_args; } else { $this->endpoints[ $route ] = array_merge( $this->endpoints[ $route ], $route_args ); } } /** * Retrieves the route map. * * The route map is an associative array with path regexes as the keys. The * value is an indexed array with the callback function/method as the first * item, and a bitmask of HTTP methods as the second item (see the class * constants). * * Each route can be mapped to more than one callback by using an array of * the indexed arrays. This allows mapping e.g. GET requests to one callback * and POST requests to another. * * Note that the path regexes (array keys) must have @ escaped, as this is * used as the delimiter with preg_match() * * @since 4.4.0 * @since 5.4.0 Added `$route_namespace` parameter. * * @param string $route_namespace Optionally, only return routes in the given namespace. * @return array `'/path/regex' => array( $callback, $bitmask )` or * `'/path/regex' => array( array( $callback, $bitmask ), ...)`. */ public function get_routes( $route_namespace = '' ) { $endpoints = $this->endpoints; if ( $route_namespace ) { $endpoints = wp_list_filter( $endpoints, array( 'namespace' => $route_namespace ) ); } /** * Filters the array of available REST API endpoints. * * @since 4.4.0 * * @param array $endpoints The available endpoints. An array of matching regex patterns, each mapped * to an array of callbacks for the endpoint. These take the format * `'/path/regex' => array( $callback, $bitmask )` or * `'/path/regex' => array( array( $callback, $bitmask ). */ $endpoints = apply_filters( 'rest_endpoints', $endpoints ); // Normalize the endpoints. $defaults = array( 'methods' => '', 'accept_json' => false, 'accept_raw' => false, 'show_in_index' => true, 'args' => array(), ); foreach ( $endpoints as $route => &$handlers ) { if ( isset( $handlers['callback'] ) ) { // Single endpoint, add one deeper. $handlers = array( $handlers ); } if ( ! isset( $this->route_options[ $route ] ) ) { $this->route_options[ $route ] = array(); } foreach ( $handlers as $key => &$handler ) { if ( ! is_numeric( $key ) ) { // Route option, move it to the options. $this->route_options[ $route ][ $key ] = $handler; unset( $handlers[ $key ] ); continue; } $handler = wp_parse_args( $handler, $defaults ); // Allow comma-separated HTTP methods. if ( is_string( $handler['methods'] ) ) { $methods = explode( ',', $handler['methods'] ); } elseif ( is_array( $handler['methods'] ) ) { $methods = $handler['methods']; } else { $methods = array(); } $handler['methods'] = array(); foreach ( $methods as $method ) { $method = strtoupper( trim( $method ) ); $handler['methods'][ $method ] = true; } } } return $endpoints; } /** * Retrieves namespaces registered on the server. * * @since 4.4.0 * * @return string[] List of registered namespaces. */ public function get_namespaces() { return array_keys( $this->namespaces ); } /** * Retrieves specified options for a route. * * @since 4.4.0 * * @param string $route Route pattern to fetch options for. * @return array|null Data as an associative array if found, or null if not found. */ public function get_route_options( $route ) { if ( ! isset( $this->route_options[ $route ] ) ) { return null; } return $this->route_options[ $route ]; } /** * Matches the request to a callback and call it. * * @since 4.4.0 * * @param WP_REST_Request $request Request to attempt dispatching. * @return WP_REST_Response Response returned by the callback. */ public function dispatch( $request ) { $this->dispatching_requests[] = $request; /** * Filters the pre-calculated result of a REST API dispatch request. * * Allow hijacking the request before dispatching by returning a non-empty. The returned value * will be used to serve the request instead. * * @since 4.4.0 * * @param mixed $result Response to replace the requested version with. Can be anything * a normal endpoint can return, or null to not hijack the request. * @param WP_REST_Server $server Server instance. * @param WP_REST_Request $request Request used to generate the response. */ $result = apply_filters( 'rest_pre_dispatch', null, $this, $request ); if ( ! empty( $result ) ) { // Normalize to either WP_Error or WP_REST_Response... $result = rest_ensure_response( $result ); // ...then convert WP_Error across. if ( is_wp_error( $result ) ) { $result = $this->error_to_response( $result ); } array_pop( $this->dispatching_requests ); return $result; } $error = null; $matched = $this->match_request_to_handler( $request ); if ( is_wp_error( $matched ) ) { $response = $this->error_to_response( $matched ); array_pop( $this->dispatching_requests ); return $response; } list( $route, $handler ) = $matched; if ( ! is_callable( $handler['callback'] ) ) { $error = new WP_Error( 'rest_invalid_handler', __( 'The handler for the route is invalid.' ), array( 'status' => 500 ) ); } if ( ! is_wp_error( $error ) ) { $check_required = $request->has_valid_params(); if ( is_wp_error( $check_required ) ) { $error = $check_required; } else { $check_sanitized = $request->sanitize_params(); if ( is_wp_error( $check_sanitized ) ) { $error = $check_sanitized; } } } $response = $this->respond_to_request( $request, $route, $handler, $error ); array_pop( $this->dispatching_requests ); return $response; } /** * Returns whether the REST server is currently dispatching / responding to a request. * * This may be a standalone REST API request, or an internal request dispatched from within a regular page load. * * @since 6.5.0 * * @return bool Whether the REST server is currently handling a request. */ public function is_dispatching() { return (bool) $this->dispatching_requests; } /** * Matches a request object to its handler. * * @access private * @since 5.6.0 * * @param WP_REST_Request $request The request object. * @return array|WP_Error The route and request handler on success or a WP_Error instance if no handler was found. */ protected function match_request_to_handler( $request ) { $method = $request->get_method(); $path = $request->get_route(); $with_namespace = array(); foreach ( $this->get_namespaces() as $namespace ) { if ( str_starts_with( trailingslashit( ltrim( $path, '/' ) ), $namespace ) ) { $with_namespace[] = $this->get_routes( $namespace ); } } if ( $with_namespace ) { $routes = array_merge( ...$with_namespace ); } else { $routes = $this->get_routes(); } foreach ( $routes as $route => $handlers ) { $match = preg_match( '@^' . $route . '$@i', $path, $matches ); if ( ! $match ) { continue; } $args = array(); foreach ( $matches as $param => $value ) { if ( ! is_int( $param ) ) { $args[ $param ] = $value; } } foreach ( $handlers as $handler ) { $callback = $handler['callback']; // Fallback to GET method if no HEAD method is registered. $checked_method = $method; if ( 'HEAD' === $method && empty( $handler['methods']['HEAD'] ) ) { $checked_method = 'GET'; } if ( empty( $handler['methods'][ $checked_method ] ) ) { continue; } if ( ! is_callable( $callback ) ) { return array( $route, $handler ); } $request->set_url_params( $args ); $request->set_attributes( $handler ); $defaults = array(); foreach ( $handler['args'] as $arg => $options ) { if ( isset( $options['default'] ) ) { $defaults[ $arg ] = $options['default']; } } $request->set_default_params( $defaults ); return array( $route, $handler ); } } return new WP_Error( 'rest_no_route', __( 'No route was found matching the URL and request method.' ), array( 'status' => 404 ) ); } /** * Dispatches the request to the callback handler. * * @access private * @since 5.6.0 * * @param WP_REST_Request $request The request object. * @param string $route The matched route regex. * @param array $handler The matched route handler. * @param WP_Error|null $response The current error object if any. * @return WP_REST_Response */ protected function respond_to_request( $request, $route, $handler, $response ) { /** * Filters the response before executing any REST API callbacks. * * Allows plugins to perform additional validation after a * request is initialized and matched to a registered route, * but before it is executed. * * Note that this filter will not be called for requests that * fail to authenticate or match to a registered route. * * @since 4.7.0 * * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. * Usually a WP_REST_Response or WP_Error. * @param array $handler Route handler used for the request. * @param WP_REST_Request $request Request used to generate the response. */ $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request ); // Check permission specified on the route. if ( ! is_wp_error( $response ) && ! empty( $handler['permission_callback'] ) ) { $permission = call_user_func( $handler['permission_callback'], $request ); if ( is_wp_error( $permission ) ) { $response = $permission; } elseif ( false === $permission || null === $permission ) { $response = new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to do that.' ), array( 'status' => rest_authorization_required_code() ) ); } } if ( ! is_wp_error( $response ) ) { /** * Filters the REST API dispatch request result. * * Allow plugins to override dispatching the request. * * @since 4.4.0 * @since 4.5.0 Added `$route` and `$handler` parameters. * * @param mixed $dispatch_result Dispatch result, will be used if not empty. * @param WP_REST_Request $request Request used to generate the response. * @param string $route Route matched for the request. * @param array $handler Route handler used for the request. */ $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler ); // Allow plugins to halt the request via this filter. if ( null !== $dispatch_result ) { $response = $dispatch_result; } else { $response = call_user_func( $handler['callback'], $request ); } } /** * Filters the response immediately after executing any REST API * callbacks. * * Allows plugins to perform any needed cleanup, for example, * to undo changes made during the {@see 'rest_request_before_callbacks'} * filter. * * Note that this filter will not be called for requests that * fail to authenticate or match to a registered route. * * Note that an endpoint's `permission_callback` can still be * called after this filter - see `rest_send_allow_header()`. * * @since 4.7.0 * * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. * Usually a WP_REST_Response or WP_Error. * @param array $handler Route handler used for the request. * @param WP_REST_Request $request Request used to generate the response. */ $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request ); if ( is_wp_error( $response ) ) { $response = $this->error_to_response( $response ); } else { $response = rest_ensure_response( $response ); } $response->set_matched_route( $route ); $response->set_matched_handler( $handler ); return $response; } /** * Returns if an error occurred during most recent JSON encode/decode. * * Strings to be translated will be in format like * "Encoding error: Maximum stack depth exceeded". * * @since 4.4.0 * * @return false|string Boolean false or string error message. */ protected function get_json_last_error() { if ( JSON_ERROR_NONE === json_last_error() ) { return false; } return json_last_error_msg(); } /** * Retrieves the site index. * * This endpoint describes the capabilities of the site. * * @since 4.4.0 * * @param WP_REST_Request $request Request data. * @return WP_REST_Response The API root index data. */ public function get_index( $request ) { // General site data. $available = array( 'name' => get_option( 'blogname' ), 'description' => get_option( 'blogdescription' ), 'url' => get_option( 'siteurl' ), 'home' => home_url(), 'gmt_offset' => get_option( 'gmt_offset' ), 'timezone_string' => get_option( 'timezone_string' ), 'page_for_posts' => (int) get_option( 'page_for_posts' ), 'page_on_front' => (int) get_option( 'page_on_front' ), 'show_on_front' => get_option( 'show_on_front' ), 'namespaces' => array_keys( $this->namespaces ), 'authentication' => array(), 'routes' => $this->get_data_for_routes( $this->get_routes(), $request['context'] ), ); // Add media processing settings for users who can upload files. if ( wp_is_client_side_media_processing_enabled() && current_user_can( 'upload_files' ) ) { // Image sizes keyed by name for client-side media processing. $available['image_sizes'] = array(); foreach ( wp_get_registered_image_subsizes() as $name => $size ) { $available['image_sizes'][ $name ] = $size; } /** This filter is documented in wp-admin/includes/image.php */ $available['image_size_threshold'] = (int) apply_filters( 'big_image_size_threshold', 2560, array( 0, 0 ), '', 0 ); /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ $available['image_strip_meta'] = (bool) apply_filters( 'image_strip_meta', true ); /* * On the server, this filter receives the decoded image's actual bit depth. * The client path never decodes the image on the server, so the filter is * applied with 16 (the maximum depth the client encoder can produce) as * both the value and the current depth. The client caps its output bit * depth at the filtered value, so a plugin lowering it (e.g. to 8) takes * effect on client-generated images too. */ /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ $available['image_max_bit_depth'] = (int) apply_filters( 'image_max_bit_depth', 16, 16 ); } $response = new WP_REST_Response( $available ); $fields = $request['_fields'] ?? ''; $fields = wp_parse_list( $fields ); if ( empty( $fields ) ) { $fields[] = '_links'; } if ( $request->has_param( '_embed' ) ) { $fields[] = '_embedded'; } if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { $response->add_link( 'help', 'https://developer.wordpress.org/rest-api/' ); $this->add_active_theme_link_to_index( $response ); $this->add_site_logo_to_index( $response ); $this->add_site_icon_to_index( $response ); } else { if ( rest_is_field_included( 'site_logo', $fields ) ) { $this->add_site_logo_to_index( $response ); } if ( rest_is_field_included( 'site_icon', $fields ) || rest_is_field_included( 'site_icon_url', $fields ) ) { $this->add_site_icon_to_index( $response ); } } /** * Filters the REST API root index data. * * This contains the data describing the API. This includes information * about supported authentication schemes, supported namespaces, routes * available on the API, and a small amount of data about the site. * * @since 4.4.0 * @since 6.0.0 Added `$request` parameter. * * @param WP_REST_Response $response Response data. * @param WP_REST_Request $request Request data. */ return apply_filters( 'rest_index', $response, $request ); } /** * Adds a link to the active theme for users who have proper permissions. * * @since 5.7.0 * * @param WP_REST_Response $response REST API response. */ protected function add_active_theme_link_to_index( WP_REST_Response $response ) { $should_add = current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ); if ( ! $should_add && current_user_can( 'edit_posts' ) ) { $should_add = true; } if ( ! $should_add ) { foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) { if ( current_user_can( $post_type->cap->edit_posts ) ) { $should_add = true; break; } } } if ( $should_add ) { $theme = wp_get_theme(); $response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) ); } } /** * Exposes the site logo through the WordPress REST API. * * This is used for fetching this information when user has no rights * to update settings. * * @since 5.8.0 * * @param WP_REST_Response $response REST API response. */ protected function add_site_logo_to_index( WP_REST_Response $response ) { $site_logo_id = get_theme_mod( 'custom_logo', 0 ); $this->add_image_to_index( $response, $site_logo_id, 'site_logo' ); } /** * Exposes the site icon through the WordPress REST API. * * This is used for fetching this information when user has no rights * to update settings. * * @since 5.9.0 * * @param WP_REST_Response $response REST API response. */ protected function add_site_icon_to_index( WP_REST_Response $response ) { $site_icon_id = get_option( 'site_icon', 0 ); $this->add_image_to_index( $response, $site_icon_id, 'site_icon' ); $response->data['site_icon_url'] = get_site_icon_url(); } /** * Exposes an image through the WordPress REST API. * This is used for fetching this information when user has no rights * to update settings. * * @since 5.9.0 * * @param WP_REST_Response $response REST API response. * @param int $image_id Image attachment ID. * @param string $type Type of Image. */ protected function add_image_to_index( WP_REST_Response $response, $image_id, $type ) { $response->data[ $type ] = (int) $image_id; if ( $image_id ) { $response->add_link( 'https://api.w.org/featuredmedia', rest_url( rest_get_route_for_post( $image_id ) ), array( 'embeddable' => true, 'type' => $type, ) ); } } /** * Retrieves the index for a namespace. * * @since 4.4.0 * * @param WP_REST_Request $request REST request instance. * @return WP_REST_Response|WP_Error WP_REST_Response instance if the index was found, * WP_Error if the namespace isn't set. */ public function get_namespace_index( $request ) { $namespace = $request['namespace']; if ( ! isset( $this->namespaces[ $namespace ] ) ) { return new WP_Error( 'rest_invalid_namespace', __( 'The specified namespace could not be found.' ), array( 'status' => 404 ) ); } $routes = $this->namespaces[ $namespace ]; $endpoints = array_intersect_key( $this->get_routes(), $routes ); $data = array( 'namespace' => $namespace, 'routes' => $this->get_data_for_routes( $endpoints, $request['context'] ), ); $response = rest_ensure_response( $data ); // Link to the root index. $response->add_link( 'up', rest_url( '/' ) ); /** * Filters the REST API namespace index data. * * This typically is just the route data for the namespace, but you can * add any data you'd like here. * * @since 4.4.0 * * @param WP_REST_Response $response Response data. * @param WP_REST_Request $request Request data. The namespace is passed as the 'namespace' parameter. */ return apply_filters( 'rest_namespace_index', $response, $request ); } /** * Retrieves the publicly-visible data for routes. * * @since 4.4.0 * * @param array $routes Routes to get data for. * @param string $context Optional. Context for data. Accepts 'view' or 'help'. Default 'view'. * @return array[] Route data to expose in indexes, keyed by route. */ public function get_data_for_routes( $routes, $context = 'view' ) { $available = array(); // Find the available routes. foreach ( $routes as $route => $callbacks ) { $data = $this->get_data_for_route( $route, $callbacks, $context ); if ( empty( $data ) ) { continue; } /** * Filters the publicly-visible data for a single REST API route. * * @since 4.4.0 * * @param array $data Publicly-visible data for the route. */ $available[ $route ] = apply_filters( 'rest_endpoints_description', $data ); } /** * Filters the publicly-visible data for REST API routes. * * This data is exposed on indexes and can be used by clients or * developers to investigate the site and find out how to use it. It * acts as a form of self-documentation. * * @since 4.4.0 * * @param array[] $available Route data to expose in indexes, keyed by route. * @param array $routes Internal route data as an associative array. */ return apply_filters( 'rest_route_data', $available, $routes ); } /** * Retrieves publicly-visible data for the route. * * @since 4.4.0 * * @param string $route Route to get data for. * @param array $callbacks Callbacks to convert to data. * @param string $context Optional. Context for the data. Accepts 'view' or 'help'. Default 'view'. * @return array|null Data for the route, or null if no publicly-visible data. */ public function get_data_for_route( $route, $callbacks, $context = 'view' ) { $data = array( 'namespace' => '', 'methods' => array(), 'endpoints' => array(), ); $allow_batch = false; if ( isset( $this->route_options[ $route ] ) ) { $options = $this->route_options[ $route ]; if ( isset( $options['namespace'] ) ) { $data['namespace'] = $options['namespace']; } $allow_batch = $options['allow_batch'] ?? false; if ( isset( $options['schema'] ) && 'help' === $context ) { $data['schema'] = call_user_func( $options['schema'] ); } } $allowed_schema_keywords = array_flip( wp_get_json_schema_allowed_keywords( 'rest-api' ) ); $route = preg_replace( '#\(\?P<(\w+?)>.*?\)#', '{$1}', $route ); foreach ( $callbacks as $callback ) { // Skip to the next route if any callback is hidden. if ( empty( $callback['show_in_index'] ) ) { continue; } $data['methods'] = array_merge( $data['methods'], array_keys( $callback['methods'] ) ); $endpoint_data = array( 'methods' => array_keys( $callback['methods'] ), ); $callback_batch = $callback['allow_batch'] ?? $allow_batch; if ( $callback_batch ) { $endpoint_data['allow_batch'] = $callback_batch; } if ( isset( $callback['args'] ) ) { $endpoint_data['args'] = array(); foreach ( $callback['args'] as $key => $opts ) { if ( is_string( $opts ) ) { $opts = array( $opts => 0 ); } elseif ( ! is_array( $opts ) ) { $opts = array(); } $arg_data = array_intersect_key( $opts, $allowed_schema_keywords ); $arg_data['required'] = ! empty( $opts['required'] ); $endpoint_data['args'][ $key ] = $arg_data; } } $data['endpoints'][] = $endpoint_data; // For non-variable routes, generate links. if ( ! str_contains( $route, '{' ) ) { $data['_links'] = array( 'self' => array( array( 'href' => rest_url( $route ), ), ), ); } } if ( empty( $data['methods'] ) ) { // No methods supported, hide the route. return null; } return $data; } /** * Gets the maximum number of requests that can be included in a batch. * * @since 5.6.0 * * @return int The maximum requests. */ protected function get_max_batch_size() { /** * Filters the maximum number of REST API requests that can be included in a batch. * * @since 5.6.0 * * @param int $max_size The maximum size. */ return apply_filters( 'rest_get_max_batch_size', 25 ); } /** * Serves the batch/v1 request. * * @since 5.6.0 * * @param WP_REST_Request $batch_request The batch request object. * @return WP_REST_Response The generated response object. */ public function serve_batch_request_v1( WP_REST_Request $batch_request ) { $requests = array(); foreach ( $batch_request['requests'] as $args ) { $parsed_url = wp_parse_url( $args['path'] ); if ( false === $parsed_url ) { $requests[] = new WP_Error( 'parse_path_failed', __( 'Could not parse the path.' ), array( 'status' => 400 ) ); continue; } $single_request = new WP_REST_Request( $args['method'] ?? 'POST', $parsed_url['path'] ); if ( ! empty( $parsed_url['query'] ) ) { $query_args = array(); wp_parse_str( $parsed_url['query'], $query_args ); $single_request->set_query_params( $query_args ); } if ( ! empty( $args['body'] ) ) { $single_request->set_body_params( $args['body'] ); } if ( ! empty( $args['headers'] ) ) { $single_request->set_headers( $args['headers'] ); } $requests[] = $single_request; } $matches = array(); $validation = array(); $has_error = false; foreach ( $requests as $single_request ) { if ( is_wp_error( $single_request ) ) { $has_error = true; $matches[] = $single_request; $validation[] = $single_request; continue; } $match = $this->match_request_to_handler( $single_request ); $matches[] = $match; $error = null; if ( is_wp_error( $match ) ) { $error = $match; } if ( ! $error ) { list( $route, $handler ) = $match; if ( isset( $handler['allow_batch'] ) ) { $allow_batch = $handler['allow_batch']; } else { $route_options = $this->get_route_options( $route ); $allow_batch = $route_options['allow_batch'] ?? false; } if ( ! is_array( $allow_batch ) || empty( $allow_batch['v1'] ) ) { $error = new WP_Error( 'rest_batch_not_allowed', __( 'The requested route does not support batch requests.' ), array( 'status' => 400 ) ); } } if ( ! $error ) { $check_required = $single_request->has_valid_params(); if ( is_wp_error( $check_required ) ) { $error = $check_required; } } if ( ! $error ) { $check_sanitized = $single_request->sanitize_params(); if ( is_wp_error( $check_sanitized ) ) { $error = $check_sanitized; } } if ( $error ) { $has_error = true; $validation[] = $error; } else { $validation[] = true; } } $responses = array(); if ( $has_error && 'require-all-validate' === $batch_request['validation'] ) { foreach ( $validation as $valid ) { if ( is_wp_error( $valid ) ) { $responses[] = $this->envelope_response( $this->error_to_response( $valid ), false )->get_data(); } else { $responses[] = null; } } return new WP_REST_Response( array( 'failed' => 'validation', 'responses' => $responses, ), WP_Http::MULTI_STATUS ); } foreach ( $requests as $i => $single_request ) { if ( is_wp_error( $single_request ) ) { $result = $this->error_to_response( $single_request ); $responses[] = $this->envelope_response( $result, false )->get_data(); continue; } $clean_request = clone $single_request; $clean_request->set_url_params( array() ); $clean_request->set_attributes( array() ); $clean_request->set_default_params( array() ); /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ $result = apply_filters( 'rest_pre_dispatch', null, $this, $clean_request ); if ( empty( $result ) ) { $match = $matches[ $i ]; $error = null; if ( is_wp_error( $validation[ $i ] ) ) { $error = $validation[ $i ]; } if ( is_wp_error( $match ) ) { $result = $this->error_to_response( $match ); } else { list( $route, $handler ) = $match; if ( ! $error && ! is_callable( $handler['callback'] ) ) { $error = new WP_Error( 'rest_invalid_handler', __( 'The handler for the route is invalid' ), array( 'status' => 500 ) ); } $result = $this->respond_to_request( $single_request, $route, $handler, $error ); } } /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ $result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $single_request ); $responses[] = $this->envelope_response( $result, false )->get_data(); } return new WP_REST_Response( array( 'responses' => $responses ), WP_Http::MULTI_STATUS ); } /** * Sends an HTTP status code. * * @since 4.4.0 * * @param int $code HTTP status. */ protected function set_status( $code ) { status_header( $code ); } /** * Sends an HTTP header. * * @since 4.4.0 * * @param string $key Header key. * @param string $value Header value. */ public function send_header( $key, $value ) { /* * Sanitize as per RFC2616 (Section 4.2): * * Any LWS that occurs between field-content MAY be replaced with a * single SP before interpreting the field value or forwarding the * message downstream. */ $value = preg_replace( '/\s+/', ' ', $value ); header( sprintf( '%s: %s', $key, $value ) ); } /** * Sends multiple HTTP headers. * * @since 4.4.0 * * @param array $headers Map of header name to header value. */ public function send_headers( $headers ) { foreach ( $headers as $key => $value ) { $this->send_header( $key, $value ); } } /** * Removes an HTTP header from the current response. * * @since 4.8.0 * * @param string $key Header key. */ public function remove_header( $key ) { header_remove( $key ); } /** * Retrieves the raw request entity (body). * * @since 4.4.0 * * @global string $HTTP_RAW_POST_DATA Raw post data. * * @return string Raw request data. */ public static function get_raw_data() { // phpcs:disable PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved global $HTTP_RAW_POST_DATA; // $HTTP_RAW_POST_DATA was deprecated in PHP 5.6 and removed in PHP 7.0. if ( ! isset( $HTTP_RAW_POST_DATA ) ) { $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' ); } return $HTTP_RAW_POST_DATA; // phpcs:enable } /** * Extracts headers from a PHP-style $_SERVER array. * * @since 4.4.0 * * @param array $server Associative array similar to `$_SERVER`. * @return array Headers extracted from the input. */ public function get_headers( $server ) { $headers = array(); // CONTENT_* headers are not prefixed with HTTP_. $additional = array( 'CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true, ); foreach ( $server as $key => $value ) { if ( str_starts_with( $key, 'HTTP_' ) ) { $headers[ substr( $key, 5 ) ] = $value; } elseif ( 'REDIRECT_HTTP_AUTHORIZATION' === $key && empty( $server['HTTP_AUTHORIZATION'] ) ) { /* * In some server configurations, the authorization header is passed in this alternate location. * Since it would not be passed in both places we do not check for both headers and resolve. */ $headers['AUTHORIZATION'] = $value; } elseif ( isset( $additional[ $key ] ) ) { $headers[ $key ] = $value; } } return $headers; } } Fun and Serious Game Festival | ZIRAN Comunicación https://www.ziran.es Comunicación Estratégica Mon, 13 Dec 2021 08:21:54 +0000 es hourly 1 https://wordpress.org/?v=7.1 https://www.ziran.es/wp-content/uploads/2020/07/favicon.svg Fun and Serious Game Festival | ZIRAN Comunicación https://www.ziran.es 32 32 Psychonauts 2, juego del año en los premios Titanium https://www.ziran.es/nota_prensa/psychonauts-2-juego-del-ano-en-los-premios-titanium/ Mon, 13 Dec 2021 08:21:54 +0000 https://www.ziran.es/?post_type=nota_prensa&p=36907 El festival de videojuegos bilbaíno celebró su segunda jornada de charlas contando con varios nombres clave de la industria del videojuego, tanto internacionales (como Lianne Papp, productora ejecutiva de Back 4 Blood, o Tim Schafer, un nombre mítico de la industria que actualmente vuelve a estar de rabiosa actualidad gracias a su impecable dirección de Psychonauts 2) como varios de los […]

La entrada Psychonauts 2, juego del año en los premios Titanium apareció primero en ZIRAN Comunicación.]]>
El festival de videojuegos bilbaíno celebró su segunda jornada de charlas contando con varios nombres clave de la industria del videojuego, tanto internacionales (como Lianne Papp, productora ejecutiva de Back 4 Blood, o Tim Schafer, un nombre mítico de la industria que actualmente vuelve a estar de rabiosa actualidad gracias a su impecable dirección de Psychonauts 2) como varios de los responsables de las producciones nacionales más aclamadas de los últimos años, como Mauricio García de The Game Kitchen, Marina González de Deconstructeam o Arturo Monedero de TLR Games, entre otros.

En esta decimoprimera edición, el festival ha regresado a la presencialidad en el BEC de Barakaldo, pero ofreciendo la posibilidad de ver todas las ponencias de forma gratuita en streaming a través de su página web, y este año, como novedad, también a través de la plataforma Steam. El festival ha apostado fuerte en esta edición por su programa de conferencias y talleres.

Este segundo día, un auditorio de nuevo lleno ha podido oír algunos valiosos consejos sobre cómo empezar en el mundo de los videojuegos por parte de personajes claves del sector, o interesantes declaraciones acerca de sus últimos proyectos y lo que la producción de los mismos ha involucrado.

En ese sentido, Tim Schafer, un veterano del sector, co-creador de títulos como Monkey Island, Grim Fandango y Day of the Tentacle, y de vuelta en 2021 con su último éxito, Psychonauts 2, comentó en su inspiradora charla que “dirigir un estudio de juegos independiente tiene muchas cosas divertidas y mucha libertad … pero también muchos terrores nocturnos y pavor existencial todos los días preguntándote si vas a cerrar el próximo mes «, a la vez que confesaba que la parte preferida de su trabajo era «inventar nuevos mundos. Es realmente lo que hemos estado haciendo; crear nuevos mundos».

Lianne Papp, productora ejecutiva de Back 4 Blood, comentaba, sobre el proceso de creación y pulido de un juego «todo el equipo de desarrollo juega a diario, y nos preguntamos continuamente ‘estamos pasándolo bien?’- es nuestra forma de saber que estamos llevando el desarrollo en la dirección correcta, ya que este es el principal objetivo de nuestro juego. Participamos todos, si no diariamente, al menos un par de veces a la semana: es muy útil porque pone tu trabajo en el contexto del juego, es una forma maravillosa de testear el trabajo individual»

La jornada del sábado terminó con la tradicional entrega de los premios Titanium a los mejores juegos del año, siendo la nueva producción de Tim Schafer, el impecable Psychonauts 2, el ganador de la categoría a Game of the Year, además de Best Narrative Design. Otro de los títulos más mencionados de 2021, It Takes Two, de Hazelight Studios, ha sido galardonado con Best Game Design, y la mejor dirección artística, Best Art Direction, ha sido para Deathloop, el último título de los franceses Arkane Lyon.

En el apartado de sonido, Metroid Dread, de los españoles MercurySteam, ha sido el título ganador de Best Sound Direction, mientras que Best Adaptation, categoría dedicada a los productos transmedia, ha sido para Arcanela serie de animación de Riot basada en el universo de League of Legends.
Las categorías de juegos indies han galardonado a Chicory: A Colorful Tale como Juego Indie Más Innovador, y Mirlo: Above the Sun como Mejor Juego Vasco.

En palabras del director del festival, Alfonso Gómez: «Cerramos la undécima edición del Fun&Serious Game Festival con muy buen sabor de boca. Volver a la presencialidad nos ha dado el impulso que necesitábamos para preparar la edición de 2022 con más energía y ganas si cabe. Quiero felicitar a los premiados y nominados por el alto nivel y calidad de sus producciones, me consta, que aunque suene a tópico, ha costado muchísimo al jurado decidir los ganadores absolutos por categoría».

LISTADO DE PREMIOS TITANIUM AWARDS 2021

GOTY
Psychonauts 2

BEST GAME DESIGN
It Takes Two

BEST NARRATIVE DESIGN
Psychonauts 2

BEST ART DIRECTION
Deathloop

BEST SOUND DIRECTION
Metroid Dread

BEST ADAPTATION
ARCANE

FS PLAY AL JUEGO INDIE MÁS INNOVADOR
Chicory: A Colorful Tale

FS PLAY AL MEJOR VIDEOJUEGO VASCO
Mirlo: Above the Sun

 

Fotos en alta del festival (acreditar autor: Koldo Larrea)

La entrada Psychonauts 2, juego del año en los premios Titanium apareció primero en ZIRAN Comunicación.]]>
Fun&Serious día 1: Una celebración del talento en la industria del videojuego https://www.ziran.es/nota_prensa/funserious-dia-1-una-celebracion-del-talento-en-la-industria-del-videojuego/ Mon, 13 Dec 2021 08:18:17 +0000 https://www.ziran.es/?post_type=nota_prensa&p=36898 El festival de videojuegos bilbaíno celebra su primera jornada de charlas contando con varios nombres clave de la industria del videojuego, como Dinga Bakaba (director de Deathloop), Luis Antonio (12 minutos), Andrew Hoffacker (Riot Forge), Raúl Rubio (Tequila) o Maye MacSwiney (Riot). En esta decimoprimera edición, el festival ha regresado a la presencialidad en el […]

La entrada Fun&Serious día 1: Una celebración del talento en la industria del videojuego apareció primero en ZIRAN Comunicación.]]>
El festival de videojuegos bilbaíno celebra su primera jornada de charlas contando con varios nombres clave de la industria del videojuego, como Dinga Bakaba (director de Deathloop), Luis Antonio (12 minutos), Andrew Hoffacker (Riot Forge), Raúl Rubio (Tequila) o Maye MacSwiney (Riot).

En esta decimoprimera edición, el festival ha regresado a la presencialidad en el BEC de Barakaldo, pero ofreciendo las posibilidad de ver todas las ponencias de forma gratuita en streaming a través de su página web, y este año, como novedad, también a través de la plataforma Steam. El festival ha apostado fuerte en esta edición por su programa de conferencias y talleres.

Este primer día, un auditorio lleno ha podido oír algunos de los valiosos consejos sobre cómo empezar en el mundo de los videojuegos por parte de personajes claves del sector, o interesantes declaraciones acerca de sus últimos proyectos y lo que la producción de los mismos ha involucrado.

Dinga Bakaba, director de Deathloop, hablaba en su charla sobre la tarea de dirigir un videojuego, y daba un consejo para aquellos que quieren formar parte de la industria: «Cuando dejas de ser ambicioso en esta industria, es muy difícil. No puedes ser conservador en este sector, al menos no por mucho tiempo. Pero si trabajar en este sector es tu pasión, si te encantan los juegos y hacer juegos, hay mucha felicidad por encontrar «.

En palabras del director del festival, Alfonso Gómez: “Esta nueva edición es la vuelta del Fun&Serious a la presencialidad, a los encuentros entre desarrolladores, estudiantes, publishers, conferenciantes…esto ha sido, sin lugar a dudas, lo más emocionante de este primer día. Sentir el cariño y compromiso de público e industria para con el Fun&Serious nos da más fuerzas para seguir trabajando en una edición 2022 que, ojalá, sea en unas condiciones más normales para todos».

La jornada del sábado terminará con la tradicional entrega de los premios Titanium a los mejores juegos del año, pero también habrá espacio para más figuras clave del sector, tanto internacionales (como Lianne Papp, productora ejecutiva de Back 4 Blood, o Tim Schafer, un nombre mítico de la industria que actualmente vuelve a estar de rabiosa actualidad gracias a su impecable dirección de Psychonauts 2) como varios de los responsables de las producciones nacionales más aclamadas de los últimos años, como Mauricio García de The Game Kitchen, Marina González de Deconstructeam o Arturo Monedero de TLR Games.

 

Descarga todas las fotos del día 1
La entrada Fun&Serious día 1: Una celebración del talento en la industria del videojuego apareció primero en ZIRAN Comunicación.]]>
Fun&Serious será el primer festival de videojuegos español en Steam https://www.ziran.es/nota_prensa/funserious-sera-el-primer-festival-de-videojuegos-espanol-en-steam/ Fri, 10 Dec 2021 08:02:40 +0000 https://www.ziran.es/?post_type=nota_prensa&p=36869 El Fun&Serious 2021 crea un espacio en la tienda online de videojuegos de PC más importante desde el que promover la industria del videojuego local, ver gratuitamente las conferencias y descubrir una selección de juegos creados por la industria del videojuego española. Es festival revela los últimos nombres incorporados a su programa de charlas y […]

La entrada Fun&Serious será el primer festival de videojuegos español en Steam apareció primero en ZIRAN Comunicación.]]>
  • El Fun&Serious 2021 crea un espacio en la tienda online de videojuegos de PC más importante desde el que promover la industria del videojuego local, ver gratuitamente las conferencias y descubrir una selección de juegos creados por la industria del videojuego española.
  • Es festival revela los últimos nombres incorporados a su programa de charlas y conferencias de este viernes y sábado.

Esta nueva edición del Fun&Serious Game Festival, uno de los festivales clave para la industria del videojuego española, al que el público podrá acudir presencialmente o de forma virtual, tendrá lugar por primera vez, también en Steam. El store online de videojuegos más importante del mundo tendrá una página del festival bilbaíno; en él se podrán ver las conferencias, pero también descubrir una selección de juegos creados por la industria del videojuego española.

Con esta iniciativa el Fun&Serious quiere impulsar más allá su labor de dar a conocer fuera de nuestras fronteras los juegos creados en España, y que se conozcan mejor aquí las obras creadas por los estudios patrios. El talento de los creadores españoles estará representado por una selección de los mejores títulos de los últimos años, tan renombrados y premiados como Blasphemous, Call of the Sea, Do Not Feed the Monkeys, Aragami 2, Grotto, Deiland… todos ellos creaciones magníficas que harán las delicias del jugador más exigente. Varios de estos títulos contarán además con un descuento especial durante el festival. También habrá demos gratuitas, tanto de juegos ya lanzados, como betas de obras actualmente en desarrollo, y un showcase de juegos por venir que el festival quiere destacar por su original propuesta y calidad.

El festival además anuncia los últimos nombres que cierran las filas de su programa de charlas: 

Maye MacSwiney, responsable de Comunicación y Estrategia de Marketing de Riot Games para España, Portugal e Italia, hablará de Arcane, la serie de animación basada en el universo de League of Legends que, desde su estreno el pasado 7 de noviembre, continúa en el top10 de Netflix en 76 países (tras haber liderado rankings de visualizaciones en 87 naciones). Profundizará en cómo la expansión transmedia de los videojuegos es uno de los caballos de batalla de la industria a nivel mundial.

Marcos García (Gammera Nest) y Gustavo Maeso (Mediaset Games), también tocarán el tema del salto transmedia hablando sobre Way Down, el videojuego basado en la película del mismo nombre. Recrear el Banco de España que filmó Jaume Balagueró para esta historia de ladrones ha sido uno de los muchos retos de desarrollo de esta experiencia atmosférica llena de suspense que es el primer lanzamiento de Mediaset Games, la filial de contenidos originales de entretenimiento digital para la explotación de licencias de cine y televisión de Mediaset España en formato videojuego junto a estudios nacionales e internacionales. Además de este título, comentarán los siguientes desarrollos, actualmente en fase de producción.

Los populares streamers Victor Matute (Pazos), Eric Rodríguez (EricRod), Samuel Molina (Fukuy) y Enoc Benítez (EnocGuitar) tendrán un encuentro en el que hablarán de la relación cada vez más estrecha entre creadores de contenido y desarrolladores de videojuegos. Entre todos los ponentes se pondrá en común los ingredientes que funcionan mejor para conseguir más audiencia y como esta puede convertirse, o no, en ventas para los videojuegos.

Luis García, de LLC Shinyuden, editora tokiota de juegos indies, hablará sobre las especiales características de este país a la hora de poder publicar allí un juego, y los pasos para lograrlo con éxito, y por último, Tatiana Delgado, Cofundadora y directora creativa de Out of the Blue games, y José Raluy, jefe de desarrollo de Tequila Works, hablarán de Blade: The Edge of Darkness, el videojuego español que revolucionó la industria nacional hace 20 años.

La entrada Fun&Serious será el primer festival de videojuegos español en Steam apareció primero en ZIRAN Comunicación.]]> DEATHLOOP Y PSYCHONAUTS 2 ACAPARAN CANDIDATURAS EN LOS PREMIOS TITANIUM 2021 https://www.ziran.es/nota_prensa/deathloop-y-psychonauts-2-acaparan-candidaturas-en-los-premios-titanium-2021/ Thu, 02 Dec 2021 14:14:18 +0000 https://www.ziran.es/?post_type=nota_prensa&p=36650 La producción española Metroid Dread, con 3 nominaciones, e It Takes Two, con 2, son otras de las de producciones que aspiran al GOTY 155 juegos procedentes de 41 países diversos se han presentado a los FS Play, que patrocina AEVI y reconocen la propuesta indie más innovadora. 10 títulos optan al Premio al Mejor […]

La entrada DEATHLOOP Y PSYCHONAUTS 2 ACAPARAN CANDIDATURAS EN LOS PREMIOS TITANIUM 2021 apareció primero en ZIRAN Comunicación.]]>
  • La producción española Metroid Dread, con 3 nominaciones, e
    It Takes Two, con 2, son otras de las de producciones que aspiran al GOTY
  • 155 juegos procedentes de 41 países diversos se han presentado a los FS Play, que patrocina AEVI y reconocen la propuesta indie más innovadora. 10 títulos optan al Premio al Mejor Videojuego Vasco
  • MADRID (2/12/21)– Fun & Serious Game Festival ha hecho público el listado de nominados a la undécima edición de sus Premios Titanium, que distinguen los mejores videojuegos internacionales y que se entregarán el sábado 11 de diciembre, a las 18.30, en el espacio Luxua, de BEC Bilbao. La gala de entrega de premios será presentada por los comunicadores Lara Smirnova y Antonio Santo.

    Psychonauts 2, Metroid Dread, Forza Horizon 5, It Takes Two y Deathloop son los cinco títulos nominados en la categoría de Videojuego del año. El festival Fun & Serious estrena este 2021 nueva categoría de premios al incluir “La mejor adaptación”, un galardón en que están nominadas las series “Arcane” -una propuesta de Riot Games y el estudio francés Fortiche sobre el universo de Runeterra-, la japonesa “Resident Evil: Oscuridad Infinita” (producida por Hiroyuki Kobayashi) así como la película de animación “The Witcher; La Pesadilla del Lobo”, todas ellas emitidas en la plataforma Netflix.

    El juego de plataformas de Double Fine que guía al pequeño Raz en una epopeya a través de las mentes, Psychonauts 2, acapara 4 nominaciones ya que también es candidato al Mejor Diseño de Juego, el Mejor diseño Narrativo y la Mejor Dirección Artística.

    El mismo número de candidaturas va al shooter de Arkane Studios y Bethesda, Deathloop, que opta a premio en idéntico elenco de categorías: GOTY; Mejor Diseño de Juego, Mejor Diseño Narrativo y Mejor dirección de arte.
    Opta a tres Titanium la producción española protagonizada por la cazarrecompensas Samus Aran, Metroid Dread (Goty, Mejor Diseño de Juego y Mejor Dirección de Sonido) mientras que es candidata a dos Titanium, It Takes Two (desarrollado por Hazelight Studios y publicado por Electronic Arts) en las categorías de GOTY y Mejor Diseño de Juego.

    Otros dos títulos optan a dos Titanium, el juego de plataformas de Insomniac Games Ratchet & Clank: Una Dimensión Aparte opta a dos Titanium en esta edición, en este caso, Mejor Diseño de Juego y Mejor Dirección de Arte. Así como el cinematográfico 12 Minutos, candidato al Mejor Diseño Narrativo y Mejor Dirección de Sonido.

    Junto a los premios Titanium, F&S otorga los FS Play al videojuego indie más innovador, unos galardones dotados con 4.000 euros, que patrocina la Asociación Española de Videojuegos, AEVI y que falla un jurado compuesto por destacados periodistas y académicos como Inés Alcolea Llopis (Geek & Videogames), Marta Trivi (AnaitGames), Daniel Muriel y José María Martínez Burgos (Hafo).

    155 videojuegos desarrollados por estudios independientes procedentes de 41 países se han presentado en esta edición. España (con 68 propuestas) ha sido la nación más representada. Junto a EEUU (10 títulos), Reino Unido (8), Francia (también 8) y Alemania (6) 10 propuestas se han presentado al Mejor Videojuego vasco, que también esponsoriza AEVI.

     

    LISTADO COMPLETO DE NOMINADOS 2021
    A LOS PREMIOS TITANIUM DE F&S GAME FESTIVAL

    GOTY
    Psychonauts 2
    Metroid Dread
    Forza Horizon 5
    Deathloop
    It Takes Two

    BEST GAME DESIGN
    Psychonauts 2
    Metroid Dread
    It Takes Two
    Deathloop
    Ratchet & Clank: Una Dimensión Aparte

    BEST NARRATIVE DESIGN
    Psychonauts 2
    12 Minutes
    Call of the Sea
    Deathloop
    Marvel’s Guardians Of The Galaxy

    BEST ART DIRECTION
    Psychonauts 2
    Deathloop
    Ratchet & Clank: Una Dimensión Aparte
    Call of the Sea
    Resident Evil Village

    BEST SOUND DIRECTION
    Metroid Dread
    Returnal
    Resident Evil Village
    Marvel’s Guardians Of The Galaxy
    12 Minutes

    BEST ADAPTATION
    ARCANE
    THE WITCHER: LA PESADILLA DEL LOBO
    RESIDENT EVIL: OSCURIDAD INFINITA

    La entrada DEATHLOOP Y PSYCHONAUTS 2 ACAPARAN CANDIDATURAS EN LOS PREMIOS TITANIUM 2021 apareció primero en ZIRAN Comunicación.]]>
    F&S Y AEVI IMPULSAN LOS PREMIOS FSPLAY A LAS MEJORES PRODUCCIONES INDEPENDIENTES https://www.ziran.es/nota_prensa/fs-y-aevi-impulsan-los-premios-fsplay-a-las-mejores-producciones-independientes/ Tue, 23 Nov 2021 16:43:13 +0000 https://www.ziran.es/?post_type=nota_prensa&p=36302 El plazo de presentación de candidaturas a los FS Play -que premian los mejores títulos indie internacionales y los mejores juegos vascos – está abierto hasta el 26 de noviembre MADRID (23/11/21)– Fun & Serious, este año en su undécima edición, relanza de la mano de AEVI (Asociación Española de Videojuegos) sus galardones FS Play, […]

    La entrada F&S Y AEVI IMPULSAN LOS PREMIOS FSPLAY A LAS MEJORES PRODUCCIONES INDEPENDIENTES apareció primero en ZIRAN Comunicación.]]>
  • El plazo de presentación de candidaturas a los FS Play -que premian los mejores títulos indie internacionales y los mejores juegos vascos – está abierto hasta el 26 de noviembre
  • MADRID (23/11/21)– Fun & Serious, este año en su undécima edición, relanza de la mano de AEVI (Asociación Española de Videojuegos) sus galardones FS Play, en busca del título indie más innovador del panorama internacional, así como de la mejor propuesta de videojuego de factura vasca.

    AEVI que lleva apoyando el festival desde su primera convocatoria, patrocina este certamen internacional de videojuegos cuya finalidad es premiar e impulsar la creatividad y la innovación en el desarrollo de videojuegos independientes. Lo hace ofreciendo tanto un apoyo económico a través de la dotación de sus premios, como garantizando la visibilidad de estos proyectos a través de reconocimiento públicos.

    “Desde la Asociación Española de Videojuegos (AEVI) celebramos poder estar un año más en la maravillosa ciudad de Bilbao para colaborar en otra edición del Festival Fun & Serious, con quienes siempre hemos remado en la misma dirección. Ambos trabajamos en impulsar a la industria del videojuego en España, promover un ecosistema que invite a la inversión extranjera y en dar a conocer el talento de nuestros creadores”, afirma José María Moreno, director general de AEVI.

     

    Según el director del festival, Alfonso Gómez, “es un inmenso placer contar con la alianza de AEVI una edición más del Festival. Su visión y trabajo, así como su apoyo a iniciativas como el certamen internacional FS Play, hacen crecer año a año al F&S y, por supuesto, al sector del videojuego español”

    Las bases de los premios -ya en su 4º edición – están disponibles en la web del festival y prevén una dotación de 4.000 euros para el mejor juego indie -de cualquier género, temática y jugable en al menos una plataforma entre PC, consola o dispositivos móviles. La dotación para la mejor propuesta vasca es también de 4.000 euros; en este caso solo se aceptarán proyectos desarrollados por estudios ubicados en Euskadi y con el 75% de su plantilla residente en esta comunidad autónoma. El vencedor de la edición de 2020, la empresa bilbaíba EduJoy Entertnaiment con ‘Blockville’ acaba de ser adquirida por el gigante Gamer Sandbox.

    El jurado de los FS Play -que estará compuesto por destacados profesionales del sector- tendrá en cuenta el diseño narrativo y artístico del juego (mecánicas, niveles, guión, world building…), así como su calidad en el aspecto sonoro, musical y de audio

    El plazo de presentación de candidaturas -que pueden formalizarse a través de este formulario online– se cierra este 26 de noviembre.

    En palabras de Arturo Monedero, vicepresidente de Desarrollo de AEVI: “Esta edición del Fun & Serious es particularmente especial tras pasar un año y medio de parón en celebración de eventos presenciales. Estamos muy ilusionados con apoyar la feria y en acudir a uno de los lugares de encuentro de los agentes de nuestra industria: desarrolladores, distribuidores, estudiantes y toda la comunidad de videojugadores”.

     

    AEVI es la principal organización del videojuego en España. Representa a más de 60 empresas y centro académicos que generan la mayoría de los puestos de trabajo del sector en nuestro país y representan el 90% del consumo del mercado español.

    La entrada F&S Y AEVI IMPULSAN LOS PREMIOS FSPLAY A LAS MEJORES PRODUCCIONES INDEPENDIENTES apareció primero en ZIRAN Comunicación.]]>
    LIANNE PAPP (BACK 4 BLOOD), ROBERT COLL (RAINBOW SIX SIEGE), MAURICIO GARCÍA (BLASPHEMOUS) Y ANDREW HOFFACKER (RIOT FORGE) TAMBIÉN ESTARÁN EN F&S https://www.ziran.es/nota_prensa/lianne-papp-back-4-blood-robert-coll-rainbow-six-siege-mauricio-garcia-blasphemous-y-andrew-hoffacker-riot-forge-tambien-estaran-en-fs/ Thu, 18 Nov 2021 12:37:58 +0000 https://www.ziran.es/?post_type=nota_prensa&p=36124 El Head of Production de Riot Forge, Andrew Hoffacker conversará en el festival con Raúl Rubio, responsable de Song of Nunu, desarrollado por Tequila Works Liane Papp, productora ejecutiva de Back 4 Blood (Warner) se une a la nómina de invitados a las conferencias del festival internacional de videojuegos Fun & Serious, que este año podrá […]

    La entrada LIANNE PAPP (BACK 4 BLOOD), ROBERT COLL (RAINBOW SIX SIEGE), MAURICIO GARCÍA (BLASPHEMOUS) Y ANDREW HOFFACKER (RIOT FORGE) TAMBIÉN ESTARÁN EN F&S apareció primero en ZIRAN Comunicación.]]>
  • El Head of Production de Riot Forge, Andrew Hoffacker conversará en el festival con Raúl Rubio, responsable de Song of Nunu, desarrollado por Tequila Works
  • Liane Papp, productora ejecutiva de Back 4 Blood (Warner) se une a la nómina de invitados a las conferencias del festival internacional de videojuegos Fun & Serious, que este año podrá seguirse tanto presencialmente, en el espacio Luxua del BEC de Bilbao, como digitalmente a través de twitch y los perfiles en redes del festival.

    Lianne Papp es productora ejecutiva de Turtle Rock Studios y dirigió la producción de Back 4 Blood, el lanzamiento más reciente del estudio. Papp se unió a Turtle Rock Studios en 2013 como productor senior y tiene más de 12 años de experiencia en programación y desarrollo de juegos, principalmente en shooters en primera persona, con un enfoque en producción y gestión.

    Las tradicionales charlas del F&S, que congregan a relevantes protagonistas de la industria, a creadores de los títulos del momento, a inversores y a desarrolladores indie, contarán asimismo con la presencia de Robert Coll, Game designer de Rainbox Six: Siege en Ubisoft. Coll hablará del proceso de diseño de un título en constante evolución por estar volcado a la competición y a un modelo de negocio que significa incorporar casi cotidianamente el feedback de centenares de miles de jugadores en todo el mundo.

    El festival también contará con la presencia de Andrew Hoffacker, Head of Production de Riot Forge, la plataforma de Riot Games que se encarga del desarrollo y lanzamiento de títulos en el universo de Runeterra desarrollados por estudios independientes. En este caso estará acompañado de los responsables de su anuncio más reciente ‘Song of Nunu’, un título desarrollado por el estudio español Tequila Works. Raúl Rubio, CEO y Director Creativo de Tequila, comentará con Hoffacker los detalles de este conmovedor videojuego de aventuras para un jugador que nos embarca en la epopeya del campeón de League of Legends Nunu, y su inseparable compañero Willump, para encontrar a la madre perdida de Nunu.

    F&S confirma asimismo la presencia de Mauricio García, director del laureado estudio español The Games Kitchen.

    Mauricio García es el productor de ‘Blasphemous’, uno de los juegos indies españoles más vendidos y récord de financiación en Kickstarter. Además, se dedica a formar a la nueva generación de emprendedores en desarrollo de videojuegos y es mentor e inversor en equipos noveles.

    Por su parte, TLR Games hará un post mortem del videojuego The Longest Road on Earth, ganador del Mejor Videojuego Vasco en 2019 y lanzado recientemente por Raw Fury, un partnership que surgió, precisamente, en la edición de 2019 de F&S.

    Las entradas para asistir tanto de forma presencial (aforo limitado) como online al evento saldrán a la venta en los próximos días.

    La entrada LIANNE PAPP (BACK 4 BLOOD), ROBERT COLL (RAINBOW SIX SIEGE), MAURICIO GARCÍA (BLASPHEMOUS) Y ANDREW HOFFACKER (RIOT FORGE) TAMBIÉN ESTARÁN EN F&S apareció primero en ZIRAN Comunicación.]]>
    DINGA BAKABA, TIM SCHAFER, LUIS ANTONIO: PRIMEROS PONENTES CONFIRMADOS DE FUN & SERIOUS https://www.ziran.es/nota_prensa/dinga-bakaba-tim-schafer-luis-antonio-primeros-ponentes-confirmados-de-fun-serious/ Thu, 11 Nov 2021 15:05:08 +0000 https://www.ziran.es/?post_type=nota_prensa&p=35913 )– Fun & Serious Game Festival retorna a Bilbao con un interesante plantel de ponentes para sus tradicionales conferencias, que se celebrarán en el espacio Luxua de BEC los días 10 y 11 de diciembre y que podrá ser seguido tanto de forma presencial como online a través de Twitch. Dinga Bakaba, Head of Studio […]

    La entrada DINGA BAKABA, TIM SCHAFER, LUIS ANTONIO: PRIMEROS PONENTES CONFIRMADOS DE FUN & SERIOUS apareció primero en ZIRAN Comunicación.]]>
    )– Fun & Serious Game Festival retorna a Bilbao con un interesante plantel de ponentes para sus tradicionales conferencias, que se celebrarán en el espacio Luxua de BEC los días 10 y 11 de diciembre y que podrá ser seguido tanto de forma presencial como online a través de Twitch.

    Dinga Bakaba, Head of Studio en Arkane Lyon y co-director creativo del videojuego Deathloop, será de uno de los ponentes destacados del ciclo de ponencias. El currículo de Bakaba incluye como títulos precedentes su trabajo como Game Designer en Gray Matter (en Wizarbox), Game Designer en Dishonored y Lead Designer para Dishonored 2 (en Arkane Lyon) así como su colaboración con Machine Games en el desarrollo de Wolfenstein: Youngblood y Wolfenstein: Cyberpilot.

    Luis Antonio, creador de uno de los títulos más interesantes del año, 12 Minutes, será otro de los ponentes más esperados. Su primer trabajo independiente, lanzado por Annapurna Interactive para PC y Xbox, es un thriller que -con claros guiños al cine de Hitchcock y Kubrik- se juega en perspectiva cenital sobre un hombre atrapado en un bucle temporal y espacial. Antonio ha trabajado con anterioridad para Rockstar Games y Ubisoft, además de con Jonathan Blow.

    También el estadounidense Tim Schafer, que recibió el Premio Honorífico del festival en 2015, será una de las participaciones más esperadas. El célebre responsable de la saga Monkey Island, ha desarrollado alguno de los títulos más importantes de Lucas Arts y, en los últimos años, como fundador de Double Fine, ha trabajado en la escritura, dirección creativa o dirección de títulos como Brutal Legends, Costume Quest, Stacking, Broken Age o, actualmente, Psychonauts 2, secuela del aclamado Psychonauts, considerada una obra de culto del videojuego.

    Por lo demás, F&S mantiene su apuesta por el desarrollo independiente dando voz a figuras relevantes de la escena. Así, en la edición de 2021, F&S contará con la participación de Marina González de Deconstructeam quien hablará de pixel art, además de Elina Roinioti de Kickstarter Games y los responsables de uno de los grandes éxitos indies del año, Eastward.

    En próximos días se pondrán a la venta las entradas para asistir tanto de forma presencial (aforo limitado) como online al evento.

    La entrada DINGA BAKABA, TIM SCHAFER, LUIS ANTONIO: PRIMEROS PONENTES CONFIRMADOS DE FUN & SERIOUS apareció primero en ZIRAN Comunicación.]]>
    Fun & Serious Game Festival regresa en formato presencial a Bilbao https://www.ziran.es/nota_prensa/fun-serious-game-festival-regresa-en-formato-presencial-a-bilbao/ Wed, 27 Oct 2021 10:03:52 +0000 https://www.ziran.es/?post_type=nota_prensa&p=35324 FUN & SERIOUS GAME FESTIVAL REGRESA EN FORMATO PRESENCIAL A BILBAO El BEC acogerá la XI edición del mayor festival Gamer de Europa los días 10 y 11 de diciembre Conferencias, workshop y encuentros profesionales se darán cita en un evento que irá desvelando sorpresas en los próximos días MADRID (27/10/21)– Tras el paréntesis digital de […]

    La entrada Fun & Serious Game Festival regresa en formato presencial a Bilbao apareció primero en ZIRAN Comunicación.]]>

    FUN & SERIOUS GAME FESTIVAL REGRESA EN FORMATO PRESENCIAL A BILBAO

    • El BEC acogerá la XI edición del mayor festival Gamer de Europa los días 10 y 11 de diciembre
    • Conferencias, workshop y encuentros profesionales se darán cita en un evento que irá desvelando sorpresas en los próximos días

    MADRID (27/10/21)– Tras el paréntesis digital de 2020, Fun & Serious Game Festival regresa en formato presencial al BEC de Bilbao. Los próximos días 10 y 11 de diciembre (viernes y sábado) el festival celebrará su XI Edicion con un aforo reducido y con una nutrida agenda de encuentros y conferencias -que también serán retransmitidos en streaming-, tanto para profesionales del sector como para aficionados y curiosos de todas las edades: un punto de encuentro para los principales agentes del sector en Europa.

    “Tras este año y medio de parón pandémico, el espíritu de este año es ser un punto de encuentro y celebración de la industria nacional -asegura Alfonso Gómez, director de Fun & Serious Game Festival- un lugar donde todos los actores de la escena española y europea: desarrolladores, publishers, prensa, distribuidores, aficionados, estudiantes… se encuentren y puedan celebrar los logros de la industria española del videojuego. Volver a encontrarnos en Bilbao es un gesto importantísimo para la industria en España y una ocasión que nos emociona profundamente”

    Las circunstancias sanitarias han evidenciado hasta qué punto el videojuego es un elemento imprescindible en la cotidianidad, una plataforma de encuentro, un arte que pone en contacto a los seres humano -tanto a nivel económico como a nivel humano-.

    Tras meses de cambios en que los que la industria Gamer y el deporte electrónico han debido- como tantos sectores productivos- readaptarse, F&S Serious pretende profundizar en el rol fundamental que el videojuego ha jugado a múltiples niveles, tanto como motor económico importantísimo en la recuperación, como como plataforma artística y como catalizador antropológico.

    Estudios internacionales e internacionales, así como profesionales de primer nivel, propondrán sus perspectivas sobre el sector en un programa que se irá desvelando los próximos días.

    Una cita estratégica

    Fun & Serious Game Festival es, hoy por hoy, una cita internacional clave para el sector del videojuego. Y pretende poner en valor sus creaciones, con una perspectiva tanto económico-estratégica, como artístico-creativa.
    Que el videojuego sea una industria clave lo evidencian sus 104.570 millones de euros como valor mundial (Statista, 2011) así como la generación de beneficios en nuestro país, en tendencia alcista y que alcanzó, el pasado año, los 1747 millones de euros. El videojuego generó en España, según datos de AEVI, más de 7.300 empleos directos y, según el Libro Blanco de AEVI, el 57% de los estudios españoles de videojuegos logró mantener a todos sus empleados, con un 36% de estudios que, durante la pandemia, logró aumentarla.
    En la actualidad, siempre de acuerdo a datos de AEVI, nuestro país alberga un total de 415 estudios a los que se unen 240 pendientes de constitución.

    La entrada Fun & Serious Game Festival regresa en formato presencial a Bilbao apareció primero en ZIRAN Comunicación.]]>
    Fun and Serious 2020: resumen 2ª jornada y declaraciones destacadas de ponentes clave https://www.ziran.es/nota_prensa/fun-and-serious-2020-resumen-2a-jornada-y-declaraciones-destacadas-de-ponentes-clave/ Sat, 12 Dec 2020 18:50:31 +0000 https://www.ziran.es/?post_type=nota_prensa&p=26707 El festival, que este año ha pasado al formato online, continuó ayer, en su segunda jornada, con las charlas de gigantes como Tim Willits, Warren Spector, Harvey Smith y Joe Madureira, entre otros.   El festival encara su tramo final: hoy sábado será la última jornada, con David Cage de Quantic Dream a la cabeza. […]

    La entrada Fun and Serious 2020: resumen 2ª jornada y declaraciones destacadas de ponentes clave apareció primero en ZIRAN Comunicación.]]>
    El festival, que este año ha pasado al formato online, continuó ayer, en su segunda jornada, con las charlas de gigantes como Tim Willits, Warren Spector, Harvey Smith y Joe Madureira, entre otros.  
    El festival encara su tramo final: hoy sábado será la última jornada, con David Cage de Quantic Dream a la cabeza.
    Toda la programación, en la web del festival.
    Con una década de existencia, y una agenda de ponentes de altísimo nivel, Fun & Serious se ha consolidado plenamente como una cita cultural imprescindible en la agenda europea, y dadas las circunstancias sanitarias actuales, ha decidido un viraje hacia lo online y en abierto, que permita celebrar su décimo aniversario por todo lo alto. Alfonso Gómez, director del festival, expresó su contento tras dos días de charlas con ponentes de altísimo nivel, “que gracias a su cambio de formato, ahora online y de libre acceso, han sido accesibles para miles de personas, dentro y fuera de nuestras fronteras”.

    En el día de ayer, segunda jornada del festival, el público pudo disfrutar de las entrevistas y charlas de nombres como Tim Willits, un nombre ligado a la saga DOOM, actualmente director de Saber Interactive,  que habló sobre el arte de presentar un juego a potenciales inversores y publishers, y que hizo las delicias de los profesionales y estudiantes del sector con sus útiles consejos:Sé que hay muchos desarrolladores jóvenes siguiendo este festival y quería darles algo realmente práctico, con lo que prosperar”.

    Warren Spector, pionero creador de Deus Ex y System Shock, comentó su visión sobre el futuro de los videojuegos, «el siguiente gran paso de esta industria será una inteligencia artificial realmente evolucionada», y celebró que la salud actual del sector sentenciando que “vivimos un boom de creatividad y energía en la industria del videojuego. Si tienes una idea hay más formas que nunca de convertirla en un juego, llegar al público y ganar dinero”.

    También ayer por la tarde se pudo ver la charla de Harvey Smith, director del estudio de Arkane en Austin, quien ha trabajado en el mundo de los videojuegos profesionalmente desde 1993, y es responsable, junto a Raphael Colantonio del juego ganador de más de 100 premios “Game of the Year” y el BAFTA 2013, Dishonored, y de las posteriores entregas de la saga. Smith habló sobre su experiencia a la hora de crear mundos ficticios, declarando que “siempre nos hacemos la misma pregunta: ¿cómo hacemos que este escenario transmita la sensación de que sus NPCs llevan una vida cotidiana en él?”

    Otro de los invitados internacionales de la jornada fue Joe Madureira, legendario artista de cómics de Marvel y CEO de Airship Syndicate, el estudio tras Ruined King: a League of Legends Story, quien declaró que fue un “auténtico shock” que Riot les propusiera hacer un RPG con la IP de League of Legends, a la vez que lo valoró como una oportunidad increíblemente estimulante. El juego resultante “permite profundizar y sumergirnos en el lore de LoL y atraer tanto fans del MOBA como gente que -aunque resulte improbable- no conoce o no ha jugado mucho al juego de PC más jugado del mundo”.

    En lo que respecta a invitados nacionales, destacaron la charla con Delphine Sassi y Carmen Hevia, de King, quienes hablaron sobre el reto que supone » mantener vivo el interés por Candy Crush desde su lanzamiento en 2012; innovar sin alterar aquello que se ha convertido en cotidiano para millones de jugadores«, la ponencia de Tatiana Delgado, de Out of the Blue Games, quién habló de su desarrollo, Call of the Sea, o la mesa redonda sobre comunicación en el sector del ocio digital, con José Herraez, PR and Communications Manager de Frontier Developments, y Maye MacSwiney, Marketing Manager de Iberia Riot Games.

    La programación del festival termina hoy sábado, con un cartel encabezado por la charla con David Cage, creador de títulos que han sido capaces de dinamitar las tradicionales fronteras del videojuego como el pluripremiado Detroit Become Human. Otras charlas serán las de Rogerio Silva, Game Design Director de Ubisoft Barcelona, quien hablará sobre la implicación de este equipo en la creación de Assassin’s Creed Valhalla, Juan González Cuín, del estudio Mindiff, quien hablará del proceso de creación y próximo lanzamiento del MOBA The Immortal Mystics, y Edu Verz, de Brainwash Gang, quien hablará de su aventura narrativa The Longest Road on Earth. Tras ellos vendrá Elesky, pianista y streamer, gran conocedora e intérprete de bandas sonoras de videojuegos.

    El festival tendrá su colofón con la entrega de los premios Titanium: La gala que distingue a los mejores títulos del año, y que podrá verse en directo vía streaming desde la web del festival, a partir de las 17:30.
    Programación completa aquí: https://www.funandseriousgamefestival.com/programa/#

    10 al 12 diciembre de 2020
    http://www.funandseriousgamefestival.com/

    Acerca de Fun & Serious Game Festival
    El Fun & Serious Game Festival, que se celebrará entre 10 y el 12 de diciembre en Bilbao, es el mayor festival de videojuegos de toda Europa y se ha convertido en un punto de referencia dentro de la industria.
    Su misión es reconocer e impulsar el trabajo de productores, directores, artistas y desarrolladores de videojuegos.
    El Fun & Serious Game Festival cuenta con el apoyo del Ayuntamiento de Bilbao, Diputación Foral de Bizkaia y del Gobierno vasco – SPRI. El patrocinio de BBK y la colaboración de la Asociación Española de Videojuegos (AEVI), GAME, BBVA y UTAD. http://www.funandseriousgamefestival.com/

     

    Enlace a fotos día 2

     

    La entrada Fun and Serious 2020: resumen 2ª jornada y declaraciones destacadas de ponentes clave apareció primero en ZIRAN Comunicación.]]>
    Fun & Serious Game Festival revela los nominados a los premios Titanium y valora su X edición como un éxito https://www.ziran.es/nota_prensa/fun-serious-game-festival-revela-los-nominados-a-los-premios-titanium-y-valora-su-x-edicion-como-un-exito/ Sat, 12 Dec 2020 18:48:18 +0000 https://www.ziran.es/?post_type=nota_prensa&p=26703 El festival ha entregado esta tarde, de modo online, sus premios a los mejores juegos del año. La gala se pudo seguir vía streaming y fue presentada por Claudio Serrano. La gala de entrega de Premios Titanium, que, como colofón del festival Fun and Serious Game Festival, distingue los mejores títulos del año, regresó en […]

    La entrada Fun & Serious Game Festival revela los nominados a los premios Titanium y valora su X edición como un éxito apareció primero en ZIRAN Comunicación.]]>
    El festival ha entregado esta tarde, de modo online, sus premios a los mejores juegos del año. La gala se pudo seguir vía streaming y fue presentada por Claudio Serrano.

    La gala de entrega de Premios Titanium, que, como colofón del festival Fun and Serious Game Festival, distingue los mejores títulos del año, regresó en la tarde del 12 diciembre al BEC de Barakaldo. Una gala que por las circunstancias actuales no ha tenido público presencial, pero que ha podido verse en directo vía streaming desde la web del festival.

    Tras tres días de charlas de algunas de las figuras más relevante del panorama internacional del videojuego, como Marc Merrill, David Cage, Harvey Smith, Tim Willits, Warren Spector o Joe Madureira, y una completa programación de talleres y ponencias en torno al gaming y la creación artística de videojuegos, con espectadores procedentes de 39 países diferentes, el festival ha dado por terminada su décima edición con la entrega de sus premios anuales. Alfonso Gómez, director del festival, ha declarado hacer “un balance muy positivo de esta edición, que aun no siendo en el contexto ideal, y con todas las limitaciones que supone el cambio al formato online, ha resultado ser todo un éxito, con 2100 registrados para ver las charlas, 30 ponentes nacionales e internacionales, 28 horas de emisión, y más de mil reuniones entre publishers, estudios independientes e inversores”. “Ya estamos pensando en la próxima edición, ojalá que el año que viene el contexto sea más benigno, y que podamos encontrarnos todos de nuevo en Bilbao para celebrar los éxitos del videojuego como motor cultural y económico. ”

    La gala de los premios Titanium,  presentada por el actor de doblaje Claudio Serrano, ha ido desgranando uno a uno los ganadores de la noche, donde The Last of Us Part II y Ori and the Will of Wisps eran claros favoritos, con cuatro nominaciones cada uno.

    El aclamado título de Sony, The Last of Us Part II se ha llevado el premio a Game Of The Year (GOTY), además de Best Narrative Design y Best OST, por su magnífica banda sonora. Por otro lado, el juego de Moon Studios para Xbox Game Studios, continuación del aclamado Ori and the Blind Forest, ha recibido el Titanium a Best Game Design y Best Art.

    En la categoría de juego más innovador se ha premiado a Arise, a simple story. El Festival también premia con un Titanium al mejor Serious Game, es decir, aquellos juegos cuyo fin no es sólo lúdico, sino que también sirven para concienciar, aprender o destacar un problema social. En esta categoría el ganador ha sido 112 Operator.

    Por último, el ganador a mejor videojuego vasco ha sido BlockVille y el Premio BBK Nuevos Talentos lo ha recibido Burn Me Twice (de UTAD).

    LISTADO DE GANADORES PREMIOS TITANIUM 2020
    GOTY

    The Last of Us Part II

    Best Game Design

    Ori and The Will of Wisps

    Best Narrative Design

    The Last of Us Part II

    Best Art

    Ori and The Will of Wisps

    Best OST

    The Last of Us Part II

    Premio FS Play al videojuego mas Innovador

    Arise, a Simple Story

    Premio FS Play al Mejor Serious Game

    112 Operator

    Premio FS Play al Mejor videojuego vasco

    Blockville

    Premio BBK Nuevos Talentos

    Burn Me Twice (UTAD)

    La celebración del Fun & Serious Game Festival sigue siendo una de las fechas clave para el sector, que subraya la relevancia estratégica, económica y creativa de una industria que genera beneficios muy importantes en nuestro país. Un sector que en 2019 facturó 1479 millones de euros, con una cifra histórica para el mercado online (con 725 millones de euros) y con una base superior a 15 millones de usuarios, según datos de AEVI en su anuario. El sector duplica ampliamente los beneficios del cine y multiplica por siete los de la música grabada en España.

    10 al 12 diciembre de 2020
    http://www.funandseriousgamefestival.com/

    Acerca de Fun & Serious Game Festival
    El Fun & Serious Game Festival, que se celebrará entre 10 y el 12 de diciembre en Bilbao, es el mayor festival de videojuegos de toda Europa y se ha convertido en un punto de referencia dentro de la industria.
    Su misión es reconocer e impulsar el trabajo de productores, directores, artistas y desarrolladores de videojuegos.
    El Fun & Serious Game Festival cuenta con el apoyo del Ayuntamiento de Bilbao, Diputación Foral de Bizkaia y del Gobierno vasco – SPRI. El patrocinio de BBK y la colaboración de la Asociación Española de Videojuegos (AEVI), GAME, BBVA y UTAD. http://www.funandseriousgamefestival.com/

     

    Descarga Fotos y nota de prensa

     

    La entrada Fun & Serious Game Festival revela los nominados a los premios Titanium y valora su X edición como un éxito apareció primero en ZIRAN Comunicación.]]>